mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge branch 'main' into specialize-exprs
This commit is contained in:
commit
2e96aca0fd
797 changed files with 17394 additions and 12632 deletions
|
@ -12,9 +12,9 @@ cargo doc --package roc_ast --open
|
|||
|
||||
The `roc` binary that brings together all functionality in the Roc toolset.
|
||||
|
||||
## `cli_utils/` - `cli_utils`
|
||||
## `cli_test_utils/` - `cli_test_utils`
|
||||
|
||||
Provides shared code for cli tests and benchmarks.
|
||||
Provides shared code for cli tests, cli benchmarks, glue tests, valgrind crate.
|
||||
|
||||
## `compiler/`
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
name = "roc_cli"
|
||||
description = "The Roc binary that brings together all functionality in the Roc toolset."
|
||||
default-run = "roc"
|
||||
build = "build.rs"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
@ -30,7 +31,13 @@ target-wasm32 = ["roc_build/target-wasm32"]
|
|||
target-x86 = ["roc_build/target-x86", "roc_repl_cli/target-x86"]
|
||||
target-x86_64 = ["roc_build/target-x86_64", "roc_repl_cli/target-x86_64"]
|
||||
|
||||
target-all = ["target-aarch64", "target-arm", "target-x86", "target-x86_64", "target-wasm32"]
|
||||
target-all = [
|
||||
"target-aarch64",
|
||||
"target-arm",
|
||||
"target-x86",
|
||||
"target-x86_64",
|
||||
"target-wasm32",
|
||||
]
|
||||
|
||||
sanitizers = ["roc_build/sanitizers"]
|
||||
|
||||
|
@ -83,15 +90,17 @@ roc_repl_expect = { path = "../repl_expect" }
|
|||
|
||||
|
||||
[dev-dependencies]
|
||||
cli_utils = { path = "../cli_utils" }
|
||||
roc_test_utils = { path = "../test_utils" }
|
||||
cli_test_utils = { path = "../cli_test_utils" }
|
||||
roc_command_utils = { path = "../utils/command" }
|
||||
|
||||
criterion.workspace = true
|
||||
indoc.workspace = true
|
||||
parking_lot.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
serial_test.workspace = true
|
||||
insta.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
chrono.workspace = true
|
||||
|
||||
[[bench]]
|
||||
name = "time_bench"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use cli_utils::bench_utils::{
|
||||
use cli_test_utils::bench_utils::{
|
||||
bench_cfold, bench_deriv, bench_nqueens, bench_quicksort, bench_rbtree_ck,
|
||||
};
|
||||
use criterion::{measurement::WallTime, BenchmarkGroup, Criterion, SamplingMode};
|
||||
|
|
71
crates/cli/build.rs
Normal file
71
crates/cli/build.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use chrono::prelude::*;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
|
||||
fn main() {
|
||||
// Rebuild if this build.rs file changes
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
// The version file is located at the root of the repository
|
||||
let version_file_path = "../../version.txt";
|
||||
|
||||
// Rebuild if version file changes
|
||||
println!("cargo:rerun-if-changed={}", version_file_path);
|
||||
|
||||
// Read the version file
|
||||
let version_file_contents = fs::read_to_string(version_file_path).unwrap();
|
||||
|
||||
// If the version is "built-from-source", replace it with the git commit information
|
||||
let version = match version_file_contents.trim() {
|
||||
"built-from-source" => {
|
||||
let git_head_file_path = "../../.git/HEAD";
|
||||
// Rebuild if a new Git commit is made
|
||||
println!("cargo:rerun-if-changed={}", git_head_file_path);
|
||||
|
||||
// Check if the .git/HEAD file exists
|
||||
let git_head_exists = fs::metadata(git_head_file_path).is_ok();
|
||||
if git_head_exists {
|
||||
// Get the hash of the current commit
|
||||
let git_describe_output = Command::new("git")
|
||||
.arg("describe")
|
||||
.arg("--always")
|
||||
.arg("--dirty= with additional changes") // Add a suffix if the working directory is dirty
|
||||
.output()
|
||||
.expect("Failed to execute git describe command");
|
||||
println!("git_describe_output: {:?}", git_describe_output);
|
||||
let git_commit_hash = str::from_utf8(&git_describe_output.stdout)
|
||||
.expect("Failed to parse git describe output")
|
||||
.trim();
|
||||
|
||||
// Get the datetime of the last commit
|
||||
let git_show_output = Command::new("git")
|
||||
.arg("show")
|
||||
.arg("--no-patch")
|
||||
.arg("--format=%ct") // Outputting a UNIX timestamp is the only way to always use UTC
|
||||
.output()
|
||||
.expect("Failed to execute git show command");
|
||||
println!("git_show_output: {:?}", git_show_output);
|
||||
let git_commit_timestamp = {
|
||||
let timestamp = str::from_utf8(&git_show_output.stdout)
|
||||
.expect("Failed to parse git show output as a string")
|
||||
.trim()
|
||||
.parse::<i64>()
|
||||
.expect("Failed to parse timestamp as an integer");
|
||||
DateTime::from_timestamp(timestamp, 0)
|
||||
.expect("Failed to parse timestamp")
|
||||
.format("%Y-%m-%d %H:%M:%S")
|
||||
};
|
||||
format!(
|
||||
"built from commit {git_commit_hash}, committed at {git_commit_timestamp} UTC"
|
||||
)
|
||||
} else {
|
||||
// If the .git/HEAD file does not exist, e.g. in a Nix build, use a generic message
|
||||
"built from source".to_string()
|
||||
}
|
||||
}
|
||||
_ => version_file_contents.trim().to_string(),
|
||||
};
|
||||
// Emit the version to a build-time environment variable
|
||||
println!("cargo:rustc-env=ROC_VERSION={}", version);
|
||||
}
|
|
@ -263,7 +263,7 @@ mod tests {
|
|||
use std::io::Write;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
const FORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
const FORMATTED_ROC: &str = r#"app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stdin
|
||||
|
@ -273,11 +273,7 @@ main =
|
|||
name = Stdin.line!
|
||||
Stdout.line! "Hi $(name)!""#;
|
||||
|
||||
const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stdin
|
||||
const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
main =
|
||||
Stdout.line! "What's your name?"
|
||||
|
|
|
@ -54,7 +54,6 @@ pub const CMD_VERSION: &str = "version";
|
|||
pub const CMD_FORMAT: &str = "format";
|
||||
pub const CMD_TEST: &str = "test";
|
||||
pub const CMD_GLUE: &str = "glue";
|
||||
pub const CMD_GEN_STUB_LIB: &str = "gen-stub-lib";
|
||||
pub const CMD_PREPROCESS_HOST: &str = "preprocess-host";
|
||||
|
||||
pub const FLAG_EMIT_LLVM_IR: &str = "emit-llvm-ir";
|
||||
|
@ -72,7 +71,8 @@ pub const FLAG_VERBOSE: &str = "verbose";
|
|||
pub const FLAG_NO_COLOR: &str = "no-color";
|
||||
pub const FLAG_NO_HEADER: &str = "no-header";
|
||||
pub const FLAG_LINKER: &str = "linker";
|
||||
pub const FLAG_PREBUILT: &str = "prebuilt-platform";
|
||||
pub const FLAG_BUILD_HOST: &str = "build-host";
|
||||
pub const FLAG_SUPPRESS_BUILD_HOST_WARNING: &str = "suppress-build-host-warning";
|
||||
pub const FLAG_CHECK: &str = "check";
|
||||
pub const FLAG_STDIN: &str = "stdin";
|
||||
pub const FLAG_STDOUT: &str = "stdout";
|
||||
|
@ -90,7 +90,7 @@ pub const FLAG_PP_HOST: &str = "host";
|
|||
pub const FLAG_PP_PLATFORM: &str = "platform";
|
||||
pub const FLAG_PP_DYLIB: &str = "lib";
|
||||
|
||||
const VERSION: &str = include_str!("../../../version.txt");
|
||||
pub const VERSION: &str = env!("ROC_VERSION");
|
||||
const DEFAULT_GENERATED_DOCS_DIR: &str = "generated-docs";
|
||||
|
||||
pub fn build_app() -> Command {
|
||||
|
@ -142,9 +142,15 @@ pub fn build_app() -> Command {
|
|||
.value_parser(["surgical", "legacy"])
|
||||
.required(false);
|
||||
|
||||
let flag_prebuilt = Arg::new(FLAG_PREBUILT)
|
||||
.long(FLAG_PREBUILT)
|
||||
.help("Assume the platform has been prebuilt and skip rebuilding the platform\n(This is enabled implicitly when using `roc build` with a --target other than `--target <current machine>`, unless the target is wasm.)")
|
||||
let flag_build_host = Arg::new(FLAG_BUILD_HOST)
|
||||
.long(FLAG_BUILD_HOST)
|
||||
.help("WARNING: platforms are responsible for building hosts, this flag will be removed when internal test platforms have a build script")
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false);
|
||||
|
||||
let flag_suppress_build_host_warning = Arg::new(FLAG_SUPPRESS_BUILD_HOST_WARNING)
|
||||
.long(FLAG_SUPPRESS_BUILD_HOST_WARNING)
|
||||
.help("WARNING: platforms are responsible for building hosts, this flag will be removed when internal test platforms have a build script")
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false);
|
||||
|
||||
|
@ -182,7 +188,7 @@ pub fn build_app() -> Command {
|
|||
PossibleValuesParser::new(Target::iter().map(Into::<&'static str>::into));
|
||||
|
||||
Command::new("roc")
|
||||
.version(concatcp!(VERSION, "\n"))
|
||||
.version(VERSION)
|
||||
.about("Run the given .roc file, if there are no compilation errors.\nYou can use one of the SUBCOMMANDS below to do something else!")
|
||||
.args_conflicts_with_subcommands(true)
|
||||
.subcommand(Command::new(CMD_BUILD)
|
||||
|
@ -201,7 +207,8 @@ pub fn build_app() -> Command {
|
|||
.arg(flag_profiling.clone())
|
||||
.arg(flag_time.clone())
|
||||
.arg(flag_linker.clone())
|
||||
.arg(flag_prebuilt.clone())
|
||||
.arg(flag_build_host.clone())
|
||||
.arg(flag_suppress_build_host_warning.clone())
|
||||
.arg(flag_fuzz.clone())
|
||||
.arg(flag_wasm_stack_size_kb)
|
||||
.arg(
|
||||
|
@ -253,7 +260,8 @@ pub fn build_app() -> Command {
|
|||
.arg(flag_profiling.clone())
|
||||
.arg(flag_time.clone())
|
||||
.arg(flag_linker.clone())
|
||||
.arg(flag_prebuilt.clone())
|
||||
.arg(flag_build_host.clone())
|
||||
.arg(flag_suppress_build_host_warning.clone())
|
||||
.arg(flag_fuzz.clone())
|
||||
.arg(
|
||||
Arg::new(FLAG_VERBOSE)
|
||||
|
@ -266,6 +274,7 @@ pub fn build_app() -> Command {
|
|||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file to test")
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.num_args(0..)
|
||||
.required(false)
|
||||
.default_value(DEFAULT_ROC_FILENAME)
|
||||
)
|
||||
|
@ -298,7 +307,8 @@ pub fn build_app() -> Command {
|
|||
.arg(flag_profiling.clone())
|
||||
.arg(flag_time.clone())
|
||||
.arg(flag_linker.clone())
|
||||
.arg(flag_prebuilt.clone())
|
||||
.arg(flag_build_host.clone())
|
||||
.arg(flag_suppress_build_host_warning.clone())
|
||||
.arg(flag_fuzz.clone())
|
||||
.arg(roc_file_to_run.clone())
|
||||
.arg(args_for_app.clone().last(true))
|
||||
|
@ -313,7 +323,8 @@ pub fn build_app() -> Command {
|
|||
.arg(flag_profiling.clone())
|
||||
.arg(flag_time.clone())
|
||||
.arg(flag_linker.clone())
|
||||
.arg(flag_prebuilt.clone())
|
||||
.arg(flag_build_host.clone())
|
||||
.arg(flag_suppress_build_host_warning.clone())
|
||||
.arg(flag_fuzz.clone())
|
||||
.arg(roc_file_to_run.clone())
|
||||
.arg(args_for_app.clone().last(true))
|
||||
|
@ -404,23 +415,6 @@ pub fn build_app() -> Command {
|
|||
.default_value(DEFAULT_ROC_FILENAME)
|
||||
)
|
||||
)
|
||||
.subcommand(Command::new(CMD_GEN_STUB_LIB)
|
||||
.about("Generate a stubbed shared library that can be used for linking a platform binary.\nThe stubbed library has prototypes, but no function bodies.\n\nNote: This command will be removed in favor of just using `roc build` once all platforms support the surgical linker")
|
||||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file for an app using the platform")
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.required(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::new(FLAG_TARGET)
|
||||
.long(FLAG_TARGET)
|
||||
.help("Choose a different target")
|
||||
.default_value(Into::<&'static str>::into(Target::default()))
|
||||
.value_parser(build_target_values_parser.clone())
|
||||
.required(false),
|
||||
)
|
||||
)
|
||||
.subcommand(Command::new(CMD_PREPROCESS_HOST)
|
||||
.about("Runs the surgical linker preprocessor to generate `.rh` and `.rm` files.")
|
||||
.arg(
|
||||
|
@ -465,7 +459,8 @@ pub fn build_app() -> Command {
|
|||
.arg(flag_profiling)
|
||||
.arg(flag_time)
|
||||
.arg(flag_linker)
|
||||
.arg(flag_prebuilt)
|
||||
.arg(flag_build_host)
|
||||
.arg(flag_suppress_build_host_warning)
|
||||
.arg(flag_fuzz)
|
||||
.arg(roc_file_to_run)
|
||||
.arg(args_for_app.trailing_var_arg(true))
|
||||
|
@ -522,18 +517,21 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
|
|||
Some(n) => Threading::AtMost(*n),
|
||||
};
|
||||
|
||||
let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
|
||||
let paths: Vec<_> = matches.get_many::<PathBuf>(ROC_FILE).unwrap().collect();
|
||||
|
||||
// Spawn the root task
|
||||
if !path.exists() {
|
||||
let current_dir = env::current_dir().unwrap();
|
||||
let expected_file_path = current_dir.join(path);
|
||||
let paths: Vec<_> = {
|
||||
let mut flatten_paths: Vec<_> = vec![];
|
||||
for path in paths.into_iter() {
|
||||
// Spawn the root task
|
||||
if !path.exists() {
|
||||
let current_dir = env::current_dir().unwrap();
|
||||
let expected_file_path = current_dir.join(path);
|
||||
|
||||
let current_dir_string = current_dir.display();
|
||||
let expected_file_path_string = expected_file_path.display();
|
||||
let current_dir_string = current_dir.display();
|
||||
let expected_file_path_string = expected_file_path.display();
|
||||
|
||||
// TODO these should use roc_reporting to display nicer error messages.
|
||||
match matches.value_source(ROC_FILE) {
|
||||
// TODO these should use roc_reporting to display nicer error messages.
|
||||
match matches.value_source(ROC_FILE) {
|
||||
Some(ValueSource::DefaultValue) => {
|
||||
eprintln!(
|
||||
"\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"
|
||||
|
@ -541,116 +539,141 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
|
|||
}
|
||||
_ => eprintln!("\nThis file was not found: {expected_file_path_string}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"),
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let arena = &arena;
|
||||
let function_kind = FunctionKind::from_env();
|
||||
|
||||
let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let load_config = LoadConfig {
|
||||
target,
|
||||
function_kind,
|
||||
// TODO: expose this from CLI?
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
threading,
|
||||
exec_mode: ExecutionMode::Test,
|
||||
};
|
||||
let load_result = roc_load::load_and_monomorphize(
|
||||
arena,
|
||||
path.to_path_buf(),
|
||||
opt_main_path.cloned(),
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
load_config,
|
||||
);
|
||||
|
||||
let mut loaded = match load_result {
|
||||
Ok(loaded) => loaded,
|
||||
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
|
||||
return handle_loading_problem(problem);
|
||||
}
|
||||
Err(LoadMonomorphizedError::ErrorModule(module)) => {
|
||||
return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false);
|
||||
process::exit(1);
|
||||
} else if path.is_dir() {
|
||||
find_all_roc_files(path, &mut flatten_paths);
|
||||
} else {
|
||||
flatten_paths.push(path.clone());
|
||||
}
|
||||
}
|
||||
flatten_paths
|
||||
};
|
||||
let problems = report_problems_monomorphized(&mut loaded);
|
||||
|
||||
let mut expectations = std::mem::take(&mut loaded.expectations);
|
||||
let mut all_files_total_failed_count = 0;
|
||||
let mut all_files_total_passed_count = 0;
|
||||
|
||||
let interns = loaded.interns.clone();
|
||||
let sources = loaded.sources.clone();
|
||||
for path in paths.iter() {
|
||||
let arena = &arena;
|
||||
let function_kind = FunctionKind::from_env();
|
||||
|
||||
let (dyn_lib, expects_by_module, layout_interner) =
|
||||
roc_repl_expect::run::expect_mono_module_to_dylib(
|
||||
arena,
|
||||
let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let load_config = LoadConfig {
|
||||
target,
|
||||
loaded,
|
||||
opt_level,
|
||||
LlvmBackendMode::CliTest,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Print warnings before running tests.
|
||||
{
|
||||
debug_assert_eq!(
|
||||
problems.errors, 0,
|
||||
"if there were errors, we would have already exited."
|
||||
function_kind,
|
||||
// TODO: expose this from CLI?
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
threading,
|
||||
exec_mode: ExecutionMode::Test,
|
||||
};
|
||||
let load_result = roc_load::load_and_monomorphize(
|
||||
arena,
|
||||
path.to_path_buf(),
|
||||
opt_main_path.cloned(),
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
load_config,
|
||||
);
|
||||
if problems.warnings > 0 {
|
||||
problems.print_error_warning_count(start_time.elapsed());
|
||||
println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "─".repeat(80));
|
||||
|
||||
let mut loaded = match load_result {
|
||||
Ok(loaded) => loaded,
|
||||
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
|
||||
return handle_loading_problem(problem);
|
||||
}
|
||||
Err(LoadMonomorphizedError::ErrorModule(module)) => {
|
||||
return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false);
|
||||
}
|
||||
};
|
||||
let problems = report_problems_monomorphized(&mut loaded);
|
||||
|
||||
let mut expectations = std::mem::take(&mut loaded.expectations);
|
||||
|
||||
let interns = loaded.interns.clone();
|
||||
let sources = loaded.sources.clone();
|
||||
|
||||
let (dyn_lib, expects_by_module, layout_interner) =
|
||||
roc_repl_expect::run::expect_mono_module_to_dylib(
|
||||
arena,
|
||||
target,
|
||||
loaded,
|
||||
opt_level,
|
||||
LlvmBackendMode::CliTest,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Print warnings before running tests.
|
||||
{
|
||||
debug_assert_eq!(
|
||||
problems.errors, 0,
|
||||
"if there were errors, we would have already exited."
|
||||
);
|
||||
if problems.warnings > 0 {
|
||||
problems.print_error_warning_count(start_time.elapsed());
|
||||
println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "─".repeat(80));
|
||||
}
|
||||
}
|
||||
|
||||
// Run the tests.
|
||||
let arena = &bumpalo::Bump::new();
|
||||
let interns = arena.alloc(interns);
|
||||
|
||||
let mut writer = std::io::stdout();
|
||||
|
||||
let mut total_failed_count = 0;
|
||||
let mut total_passed_count = 0;
|
||||
|
||||
let mut results_by_module = Vec::new();
|
||||
let global_layout_interner = layout_interner.into_global();
|
||||
|
||||
let compilation_duration = start_time.elapsed();
|
||||
|
||||
for (module_id, expects) in expects_by_module.into_iter() {
|
||||
let test_start_time = Instant::now();
|
||||
|
||||
let (failed_count, passed_count) = roc_repl_expect::run::run_toplevel_expects(
|
||||
&mut writer,
|
||||
roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
arena,
|
||||
interns,
|
||||
&global_layout_interner,
|
||||
&dyn_lib,
|
||||
&mut expectations,
|
||||
expects,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tests_duration = test_start_time.elapsed();
|
||||
|
||||
results_by_module.push(ModuleTestResults {
|
||||
module_id,
|
||||
failed_count,
|
||||
passed_count,
|
||||
tests_duration,
|
||||
});
|
||||
|
||||
total_failed_count += failed_count;
|
||||
total_passed_count += passed_count;
|
||||
}
|
||||
|
||||
let total_duration = start_time.elapsed();
|
||||
all_files_total_failed_count += total_failed_count;
|
||||
all_files_total_passed_count += total_passed_count;
|
||||
if total_failed_count == 0 && total_passed_count == 0 {
|
||||
// Only report no expectations found once.
|
||||
continue;
|
||||
} else if matches.get_flag(FLAG_VERBOSE) {
|
||||
println!("Compiled in {} ms.", compilation_duration.as_millis());
|
||||
for module_test_results in results_by_module {
|
||||
print_test_results(module_test_results, &sources);
|
||||
}
|
||||
} else {
|
||||
let test_summary_str =
|
||||
test_summary(total_failed_count, total_passed_count, total_duration);
|
||||
println!("{test_summary_str}");
|
||||
}
|
||||
}
|
||||
|
||||
// Run the tests.
|
||||
let arena = &bumpalo::Bump::new();
|
||||
let interns = arena.alloc(interns);
|
||||
|
||||
let mut writer = std::io::stdout();
|
||||
|
||||
let mut total_failed_count = 0;
|
||||
let mut total_passed_count = 0;
|
||||
|
||||
let mut results_by_module = Vec::new();
|
||||
let global_layout_interner = layout_interner.into_global();
|
||||
|
||||
let compilation_duration = start_time.elapsed();
|
||||
|
||||
for (module_id, expects) in expects_by_module.into_iter() {
|
||||
let test_start_time = Instant::now();
|
||||
|
||||
let (failed_count, passed_count) = roc_repl_expect::run::run_toplevel_expects(
|
||||
&mut writer,
|
||||
roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
arena,
|
||||
interns,
|
||||
&global_layout_interner,
|
||||
&dyn_lib,
|
||||
&mut expectations,
|
||||
expects,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tests_duration = test_start_time.elapsed();
|
||||
|
||||
results_by_module.push(ModuleTestResults {
|
||||
module_id,
|
||||
failed_count,
|
||||
passed_count,
|
||||
tests_duration,
|
||||
});
|
||||
|
||||
total_failed_count += failed_count;
|
||||
total_passed_count += passed_count;
|
||||
}
|
||||
|
||||
let total_duration = start_time.elapsed();
|
||||
|
||||
if total_failed_count == 0 && total_passed_count == 0 {
|
||||
if all_files_total_failed_count == 0 && all_files_total_passed_count == 0 {
|
||||
// TODO print this in a more nicely formatted way!
|
||||
println!("No expectations were found.");
|
||||
|
||||
|
@ -661,18 +684,32 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
|
|||
// running tests altogether!
|
||||
Ok(2)
|
||||
} else {
|
||||
if matches.get_flag(FLAG_VERBOSE) {
|
||||
println!("Compiled in {} ms.", compilation_duration.as_millis());
|
||||
for module_test_results in results_by_module {
|
||||
print_test_results(module_test_results, &sources);
|
||||
}
|
||||
} else {
|
||||
let test_summary_str =
|
||||
test_summary(total_failed_count, total_passed_count, total_duration);
|
||||
println!("{test_summary_str}");
|
||||
}
|
||||
Ok((all_files_total_failed_count > 0) as i32)
|
||||
}
|
||||
}
|
||||
|
||||
Ok((total_failed_count > 0) as i32)
|
||||
fn find_all_roc_files(path: &PathBuf, flatten_paths: &mut Vec<PathBuf>) {
|
||||
if path.is_dir() {
|
||||
if let Ok(entries) = std::fs::read_dir(path) {
|
||||
entries.for_each(|entry| {
|
||||
if let Ok(entry) = entry {
|
||||
let entry_path = entry.path();
|
||||
find_all_roc_files(&entry_path, flatten_paths);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
eprintln!(
|
||||
"\nSomething went wrong opening the directory {}\n",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
} else if path.is_file() {
|
||||
match path.extension() {
|
||||
Some(extension) if extension == "roc" => {
|
||||
flatten_paths.push(path.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -731,7 +768,6 @@ pub fn build(
|
|||
roc_cache_dir: RocCacheDir<'_>,
|
||||
link_type: LinkType,
|
||||
) -> io::Result<i32> {
|
||||
use roc_build::program::build_file;
|
||||
use BuildConfig::*;
|
||||
|
||||
let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
|
||||
|
@ -879,17 +915,10 @@ pub fn build(
|
|||
LinkingStrategy::Surgical
|
||||
};
|
||||
|
||||
let prebuilt = {
|
||||
let cross_compile = target != Target::default();
|
||||
let targeting_wasm = matches!(target.architecture(), Architecture::Wasm32);
|
||||
|
||||
matches.get_flag(FLAG_PREBUILT) ||
|
||||
// When compiling for a different target, assume a prebuilt platform.
|
||||
// Otherwise compilation would most likely fail because many toolchains
|
||||
// assume you're compiling for the current machine. We make an exception
|
||||
// for Wasm, because cross-compiling is the norm in that case.
|
||||
(cross_compile && !targeting_wasm)
|
||||
};
|
||||
// All hosts should be prebuilt, this flag keeps the rebuilding behvaiour
|
||||
// as required for internal tests
|
||||
let build_host = matches.get_flag(FLAG_BUILD_HOST);
|
||||
let suppress_build_host_warning = matches.get_flag(FLAG_SUPPRESS_BUILD_HOST_WARNING);
|
||||
|
||||
let fuzz = matches.get_flag(FLAG_FUZZ);
|
||||
if fuzz && !matches!(code_gen_backend, CodeGenBackend::Llvm(_)) {
|
||||
|
@ -917,7 +946,7 @@ pub fn build(
|
|||
|
||||
let load_config = standard_load_config(target, build_ordering, threading);
|
||||
|
||||
let res_binary_path = build_file(
|
||||
let res_binary_path = roc_build::program::build_file(
|
||||
&arena,
|
||||
target,
|
||||
path.to_owned(),
|
||||
|
@ -925,7 +954,8 @@ pub fn build(
|
|||
emit_timings,
|
||||
link_type,
|
||||
linking_strategy,
|
||||
prebuilt,
|
||||
build_host,
|
||||
suppress_build_host_warning,
|
||||
wasm_dev_stack_bytes,
|
||||
roc_cache_dir,
|
||||
load_config,
|
||||
|
@ -986,7 +1016,15 @@ pub fn build(
|
|||
// ManuallyDrop will leak the bytes because we don't drop manually
|
||||
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
|
||||
|
||||
roc_run(&arena, opt_level, target, args, bytes, expect_metadata)
|
||||
roc_run(
|
||||
&arena,
|
||||
path,
|
||||
opt_level,
|
||||
target,
|
||||
args,
|
||||
bytes,
|
||||
expect_metadata,
|
||||
)
|
||||
}
|
||||
BuildAndRunIfNoErrors => {
|
||||
if problems.fatally_errored {
|
||||
|
@ -1021,7 +1059,15 @@ pub fn build(
|
|||
// ManuallyDrop will leak the bytes because we don't drop manually
|
||||
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
|
||||
|
||||
roc_run(&arena, opt_level, target, args, bytes, expect_metadata)
|
||||
roc_run(
|
||||
&arena,
|
||||
path,
|
||||
opt_level,
|
||||
target,
|
||||
args,
|
||||
bytes,
|
||||
expect_metadata,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1034,6 +1080,7 @@ pub fn build(
|
|||
|
||||
fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
||||
arena: &Bump,
|
||||
script_path: &Path,
|
||||
opt_level: OptLevel,
|
||||
target: Target,
|
||||
args: I,
|
||||
|
@ -1073,7 +1120,14 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
|||
|
||||
Ok(0)
|
||||
}
|
||||
_ => roc_run_native(arena, opt_level, args, binary_bytes, expect_metadata),
|
||||
_ => roc_run_native(
|
||||
arena,
|
||||
script_path,
|
||||
opt_level,
|
||||
args,
|
||||
binary_bytes,
|
||||
expect_metadata,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1090,7 +1144,7 @@ fn os_str_as_utf8_bytes(os_str: &OsStr) -> &[u8] {
|
|||
|
||||
fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
arena: &'a Bump,
|
||||
executable: &ExecutableFile,
|
||||
script_path: &Path,
|
||||
args: I,
|
||||
) -> (
|
||||
bumpalo::collections::Vec<'a, CString>,
|
||||
|
@ -1098,8 +1152,7 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
|||
) {
|
||||
use bumpalo::collections::CollectIn;
|
||||
|
||||
let path = executable.as_path();
|
||||
let path_cstring = CString::new(os_str_as_utf8_bytes(path.as_os_str())).unwrap();
|
||||
let path_cstring = CString::new(os_str_as_utf8_bytes(script_path.as_os_str())).unwrap();
|
||||
|
||||
// argv is an array of pointers to strings passed to the new program
|
||||
// as its command-line arguments. By convention, the first of these
|
||||
|
@ -1137,6 +1190,7 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
|||
#[cfg(target_family = "unix")]
|
||||
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
arena: &Bump,
|
||||
script_path: &Path,
|
||||
opt_level: OptLevel,
|
||||
args: I,
|
||||
binary_bytes: &[u8],
|
||||
|
@ -1145,7 +1199,7 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
|||
use bumpalo::collections::CollectIn;
|
||||
|
||||
let executable = roc_run_executable_file_path(binary_bytes)?;
|
||||
let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, &executable, args);
|
||||
let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, script_path, args);
|
||||
|
||||
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
|
||||
.iter()
|
||||
|
@ -1400,6 +1454,7 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<Executab
|
|||
#[cfg(not(target_family = "unix"))]
|
||||
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||
arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
||||
script_path: &Path,
|
||||
opt_level: OptLevel,
|
||||
args: I,
|
||||
binary_bytes: &[u8],
|
||||
|
@ -1411,7 +1466,7 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
|||
let executable = roc_run_executable_file_path(binary_bytes)?;
|
||||
|
||||
// TODO forward the arguments
|
||||
let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args);
|
||||
let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, script_path, args);
|
||||
|
||||
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
|
||||
.iter()
|
||||
|
|
|
@ -4,27 +4,25 @@ use roc_build::link::LinkType;
|
|||
use roc_build::program::{check_file, CodeGenBackend};
|
||||
use roc_cli::{
|
||||
build_app, format_files, format_src, test, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK,
|
||||
CMD_DEV, CMD_DOCS, CMD_FORMAT, CMD_GEN_STUB_LIB, CMD_GLUE, CMD_PREPROCESS_HOST, CMD_REPL,
|
||||
CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB, FLAG_MAIN,
|
||||
FLAG_NO_COLOR, FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT, FLAG_PP_DYLIB, FLAG_PP_HOST,
|
||||
FLAG_PP_PLATFORM, FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME, GLUE_DIR, GLUE_SPEC,
|
||||
ROC_FILE,
|
||||
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, FLAG_NO_COLOR,
|
||||
FLAG_NO_HEADER, FLAG_NO_LINK, FLAG_OUTPUT, FLAG_PP_DYLIB, FLAG_PP_HOST, FLAG_PP_PLATFORM,
|
||||
FLAG_STDIN, FLAG_STDOUT, FLAG_TARGET, FLAG_TIME, GLUE_DIR, GLUE_SPEC, ROC_FILE, VERSION,
|
||||
};
|
||||
use roc_docs::generate_docs_html;
|
||||
use roc_error_macros::user_error;
|
||||
use roc_gen_dev::AssemblyBackendMode;
|
||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||
use roc_load::{FunctionKind, LoadingProblem, Threading};
|
||||
use roc_load::{LoadingProblem, Threading};
|
||||
use roc_packaging::cache::{self, RocCacheDir};
|
||||
use roc_target::Target;
|
||||
use std::fs::{self, FileType};
|
||||
use std::io::BufRead;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
#[macro_use]
|
||||
extern crate const_format;
|
||||
use tempfile::Builder;
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||
|
@ -52,7 +50,7 @@ fn main() -> io::Result<()> {
|
|||
BuildConfig::BuildAndRunIfNoErrors,
|
||||
Triple::host().into(),
|
||||
None,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
LinkType::Executable,
|
||||
)
|
||||
} else {
|
||||
|
@ -67,7 +65,7 @@ fn main() -> io::Result<()> {
|
|||
BuildConfig::BuildAndRun,
|
||||
Triple::host().into(),
|
||||
None,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
LinkType::Executable,
|
||||
)
|
||||
} else {
|
||||
|
@ -93,7 +91,7 @@ fn main() -> io::Result<()> {
|
|||
BuildConfig::BuildAndRunIfNoErrors,
|
||||
Triple::host().into(),
|
||||
None,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
LinkType::Executable,
|
||||
)
|
||||
} else {
|
||||
|
@ -121,21 +119,6 @@ fn main() -> io::Result<()> {
|
|||
Ok(1)
|
||||
}
|
||||
}
|
||||
Some((CMD_GEN_STUB_LIB, matches)) => {
|
||||
let input_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
|
||||
let target = matches
|
||||
.get_one::<String>(FLAG_TARGET)
|
||||
.and_then(|s| Target::from_str(s).ok())
|
||||
.unwrap_or_default();
|
||||
let function_kind = FunctionKind::from_env();
|
||||
roc_linker::generate_stub_lib(
|
||||
input_path,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
target,
|
||||
function_kind,
|
||||
);
|
||||
Ok(0)
|
||||
}
|
||||
Some((CMD_PREPROCESS_HOST, matches)) => {
|
||||
let preprocess_host_err =
|
||||
{ |msg: String| user_error!("\n\n ERROR PRE-PROCESSING HOST: {}\n\n", msg) };
|
||||
|
@ -170,10 +153,14 @@ fn main() -> io::Result<()> {
|
|||
|
||||
let verbose_and_time = matches.get_one::<bool>(roc_cli::FLAG_VERBOSE).unwrap();
|
||||
|
||||
let preprocessed_path = platform_path.with_file_name(target.prebuilt_surgical_host());
|
||||
let metadata_path = platform_path.with_file_name(target.metadata_file_name());
|
||||
|
||||
roc_linker::preprocess_host(
|
||||
target,
|
||||
host_path,
|
||||
platform_path,
|
||||
metadata_path.as_path(),
|
||||
preprocessed_path.as_path(),
|
||||
dylib_path,
|
||||
*verbose_and_time,
|
||||
*verbose_and_time,
|
||||
|
@ -202,7 +189,7 @@ fn main() -> io::Result<()> {
|
|||
BuildConfig::BuildOnly,
|
||||
target,
|
||||
out_path,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
link_type,
|
||||
)?)
|
||||
}
|
||||
|
@ -220,26 +207,89 @@ fn main() -> io::Result<()> {
|
|||
|
||||
let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
|
||||
|
||||
match check_file(
|
||||
&arena,
|
||||
roc_file_path.to_owned(),
|
||||
opt_main_path.cloned(),
|
||||
emit_timings,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
threading,
|
||||
) {
|
||||
Ok((problems, total_time)) => {
|
||||
problems.print_error_warning_count(total_time);
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
match roc_file_path.extension().and_then(OsStr::to_str) {
|
||||
Some("md") => {
|
||||
// Extract the blocks of roc code
|
||||
let file = fs::File::open(roc_file_path.as_path())?;
|
||||
let markdown_file_reader = io::BufReader::new(file);
|
||||
let mut roc_blocks: Vec<String> = Vec::new();
|
||||
let mut in_roc_block: bool = false;
|
||||
let mut current_block = String::new();
|
||||
|
||||
Err(LoadingProblem::FormattedReport(report)) => {
|
||||
print!("{report}");
|
||||
for line in markdown_file_reader.lines() {
|
||||
let line = line.unwrap();
|
||||
if line == "```roc" {
|
||||
in_roc_block = true;
|
||||
} else if (line == "```") & in_roc_block {
|
||||
in_roc_block = false;
|
||||
roc_blocks.push(current_block);
|
||||
current_block = String::new();
|
||||
} else if in_roc_block {
|
||||
current_block.push_str(&line);
|
||||
current_block.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
Ok(1)
|
||||
// now check each block, we exit early if any single block does not check
|
||||
let mut exit_code = 0;
|
||||
|
||||
for block in roc_blocks.iter() {
|
||||
let mut file = Builder::new().suffix(".roc").tempfile()?;
|
||||
write!(file, "{}", block)?;
|
||||
|
||||
match check_file(
|
||||
&arena,
|
||||
file.path().to_owned(),
|
||||
opt_main_path.cloned(),
|
||||
emit_timings,
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
threading,
|
||||
) {
|
||||
Ok((problems, total_time)) => {
|
||||
problems.print_error_warning_count(total_time);
|
||||
exit_code = problems.exit_code();
|
||||
}
|
||||
|
||||
Err(LoadingProblem::FormattedReport(report)) => {
|
||||
print!("{report}");
|
||||
|
||||
exit_code = 1;
|
||||
}
|
||||
Err(other) => {
|
||||
panic!("build_file failed with error:\n{other:?}");
|
||||
}
|
||||
}
|
||||
|
||||
if exit_code != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(exit_code)
|
||||
}
|
||||
Err(other) => {
|
||||
panic!("build_file failed with error:\n{other:?}");
|
||||
_ => {
|
||||
match check_file(
|
||||
&arena,
|
||||
roc_file_path.to_owned(),
|
||||
opt_main_path.cloned(),
|
||||
emit_timings,
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
threading,
|
||||
) {
|
||||
Ok((problems, total_time)) => {
|
||||
problems.print_error_warning_count(total_time);
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
|
||||
Err(LoadingProblem::FormattedReport(report)) => {
|
||||
print!("{report}");
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
Err(other) => {
|
||||
panic!("build_file failed with error:\n{other:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -364,11 +414,7 @@ fn main() -> io::Result<()> {
|
|||
Ok(format_exit_code)
|
||||
}
|
||||
Some((CMD_VERSION, _)) => {
|
||||
print!(
|
||||
"{}",
|
||||
concatcp!("roc ", include_str!("../../../version.txt"))
|
||||
);
|
||||
|
||||
println!("roc {}", VERSION);
|
||||
Ok(0)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
# see https://github.com/roc-lang/roc/issues/985
|
||||
main : Task {} []
|
||||
main = closure1 {}
|
||||
# |> Task.after (\_ -> closure2 {})
|
||||
# |> Task.after (\_ -> closure3 {})
|
||||
# |> Task.after (\_ -> closure4 {})
|
||||
main =
|
||||
closure1 {}
|
||||
|> Task.await (\_ -> closure2 {})
|
||||
|> Task.await (\_ -> closure3 {})
|
||||
|> Task.await (\_ -> closure4 {})
|
||||
# ---
|
||||
closure1 : {} -> Task {} []
|
||||
closure1 = \_ ->
|
||||
|
@ -17,32 +17,32 @@ toUnitBorrowed = \x -> Str.countUtf8Bytes x
|
|||
foo = \f, x -> f x
|
||||
|
||||
# ---
|
||||
# closure2 : {} -> Task.Task {} []
|
||||
# closure2 = \_ ->
|
||||
# x : Str
|
||||
# x = "a long string such that it's malloced"
|
||||
#
|
||||
# Task.succeed {}
|
||||
# |> Task.map (\_ -> x)
|
||||
# |> Task.map toUnit
|
||||
#
|
||||
# toUnit = \_ -> {}
|
||||
#
|
||||
closure2 : {} -> Task {} []
|
||||
closure2 = \_ ->
|
||||
x : Str
|
||||
x = "a long string such that it's malloced"
|
||||
|
||||
Task.ok {}
|
||||
|> Task.map (\_ -> x)
|
||||
|> Task.map toUnit
|
||||
|
||||
toUnit = \_ -> {}
|
||||
|
||||
# # ---
|
||||
# closure3 : {} -> Task.Task {} []
|
||||
# closure3 = \_ ->
|
||||
# x : Str
|
||||
# x = "a long string such that it's malloced"
|
||||
#
|
||||
# Task.succeed {}
|
||||
# |> Task.after (\_ -> Task.succeed x |> Task.map (\_ -> {}))
|
||||
#
|
||||
closure3 : {} -> Task {} []
|
||||
closure3 = \_ ->
|
||||
x : Str
|
||||
x = "a long string such that it's malloced"
|
||||
|
||||
Task.ok {}
|
||||
|> Task.await (\_ -> Task.ok x |> Task.map (\_ -> {}))
|
||||
|
||||
# # ---
|
||||
# closure4 : {} -> Task.Task {} []
|
||||
# closure4 = \_ ->
|
||||
# x : Str
|
||||
# x = "a long string such that it's malloced"
|
||||
#
|
||||
# Task.succeed {}
|
||||
# |> Task.after (\_ -> Task.succeed x)
|
||||
# |> Task.map (\_ -> {})
|
||||
closure4 : {} -> Task {} []
|
||||
closure4 = \_ ->
|
||||
x : Str
|
||||
x = "a long string such that it's malloced"
|
||||
|
||||
Task.ok {}
|
||||
|> Task.await (\_ -> Task.ok x)
|
||||
|> Task.map (\_ -> {})
|
||||
|
|
3
crates/cli/tests/benchmarks/platform/app.roc
Normal file
3
crates/cli/tests/benchmarks/platform/app.roc
Normal file
|
@ -0,0 +1,3 @@
|
|||
app [main] { pf: platform "main.roc" }
|
||||
|
||||
main = Task.ok {}
|
|
@ -112,8 +112,6 @@ comptime {
|
|||
const Unit = extern struct {};
|
||||
|
||||
pub fn main() !u8 {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
// The size might be zero; if so, make it at least 8 so that we don't have a nullptr
|
||||
const size = @max(@as(usize, @intCast(roc__mainForHost_1_exposed_size())), 8);
|
||||
const raw_output = roc_alloc(@as(usize, @intCast(size)), @alignOf(u64)).?;
|
||||
|
@ -123,26 +121,15 @@ pub fn main() !u8 {
|
|||
roc_dealloc(raw_output, @alignOf(u64));
|
||||
}
|
||||
|
||||
var timer = std.time.Timer.start() catch unreachable;
|
||||
|
||||
roc__mainForHost_1_exposed_generic(output);
|
||||
|
||||
const closure_data_pointer = @as([*]u8, @ptrCast(output));
|
||||
|
||||
call_the_closure(closure_data_pointer);
|
||||
|
||||
const nanos = timer.read();
|
||||
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
|
||||
|
||||
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn to_seconds(tms: std.os.timespec) f64 {
|
||||
return @as(f64, @floatFromInt(tms.tv_sec)) + (@as(f64, @floatFromInt(tms.tv_nsec)) / 1_000_000_000.0);
|
||||
}
|
||||
|
||||
fn call_the_closure(closure_data_pointer: [*]u8) void {
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
|
|
|
@ -1,255 +0,0 @@
|
|||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.PlatformTasks
|
||||
|
||||
Color : [Red, Black]
|
||||
|
||||
Tree a b : [Leaf, Node Color (Tree a b) a b (Tree a b)]
|
||||
|
||||
Map : Tree I64 Bool
|
||||
|
||||
ConsList a : [Nil, Cons a (ConsList a)]
|
||||
|
||||
main : Task {} []
|
||||
main =
|
||||
{ value, isError } = PlatformTasks.getInt!
|
||||
inputResult =
|
||||
if isError then
|
||||
Err GetIntError
|
||||
else
|
||||
Ok value
|
||||
|
||||
when inputResult is
|
||||
Ok n ->
|
||||
m = makeMap n # koka original n = 4_200_000
|
||||
val = fold (\_, v, r -> if v then r + 1 else r) m 0
|
||||
|
||||
val
|
||||
|> Num.toStr
|
||||
|> PlatformTasks.putLine
|
||||
|
||||
Err GetIntError ->
|
||||
PlatformTasks.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
boom : Str -> a
|
||||
boom = \_ -> boom ""
|
||||
|
||||
makeMap : I64 -> Map
|
||||
makeMap = \n ->
|
||||
makeMapHelp n n Leaf
|
||||
|
||||
makeMapHelp : I64, I64, Map -> Map
|
||||
makeMapHelp = \total, n, m ->
|
||||
when n is
|
||||
0 -> m
|
||||
_ ->
|
||||
n1 = n - 1
|
||||
|
||||
powerOf10 =
|
||||
n |> Num.isMultipleOf 10
|
||||
|
||||
t1 = insert m n powerOf10
|
||||
|
||||
isFrequency =
|
||||
n |> Num.isMultipleOf 4
|
||||
|
||||
key = n1 + ((total - n1) // 5)
|
||||
t2 = if isFrequency then delete t1 key else t1
|
||||
|
||||
makeMapHelp total n1 t2
|
||||
|
||||
fold : (a, b, omega -> omega), Tree a b, omega -> omega
|
||||
fold = \f, tree, b ->
|
||||
when tree is
|
||||
Leaf -> b
|
||||
Node _ l k v r -> fold f r (f k v (fold f l b))
|
||||
|
||||
depth : Tree * * -> I64
|
||||
depth = \tree ->
|
||||
when tree is
|
||||
Leaf -> 1
|
||||
Node _ l _ _ r -> 1 + depth l + depth r
|
||||
|
||||
insert : Map, I64, Bool -> Map
|
||||
insert = \t, k, v -> if isRed t then setBlack (ins t k v) else ins t k v
|
||||
|
||||
setBlack : Tree a b -> Tree a b
|
||||
setBlack = \tree ->
|
||||
when tree is
|
||||
Node _ l k v r -> Node Black l k v r
|
||||
_ -> tree
|
||||
|
||||
isRed : Tree a b -> Bool
|
||||
isRed = \tree ->
|
||||
when tree is
|
||||
Node Red _ _ _ _ -> Bool.true
|
||||
_ -> Bool.false
|
||||
|
||||
ins : Tree I64 Bool, I64, Bool -> Tree I64 Bool
|
||||
ins = \tree, kx, vx ->
|
||||
when tree is
|
||||
Leaf ->
|
||||
Node Red Leaf kx vx Leaf
|
||||
|
||||
Node Red a ky vy b ->
|
||||
when Num.compare kx ky is
|
||||
LT -> Node Red (ins a kx vx) ky vy b
|
||||
GT -> Node Red a ky vy (ins b kx vx)
|
||||
EQ -> Node Red a ky vy (ins b kx vx)
|
||||
|
||||
Node Black a ky vy b ->
|
||||
when Num.compare kx ky is
|
||||
LT ->
|
||||
when isRed a is
|
||||
Bool.true -> balanceLeft (ins a kx vx) ky vy b
|
||||
Bool.false -> Node Black (ins a kx vx) ky vy b
|
||||
|
||||
GT ->
|
||||
when isRed b is
|
||||
Bool.true -> balanceRight a ky vy (ins b kx vx)
|
||||
Bool.false -> Node Black a ky vy (ins b kx vx)
|
||||
|
||||
EQ ->
|
||||
Node Black a kx vx b
|
||||
|
||||
balanceLeft : Tree a b, a, b, Tree a b -> Tree a b
|
||||
balanceLeft = \l, k, v, r ->
|
||||
when l is
|
||||
Leaf ->
|
||||
Leaf
|
||||
|
||||
Node _ (Node Red lx kx vx rx) ky vy ry ->
|
||||
Node Red (Node Black lx kx vx rx) ky vy (Node Black ry k v r)
|
||||
|
||||
Node _ ly ky vy (Node Red lx kx vx rx) ->
|
||||
Node Red (Node Black ly ky vy lx) kx vx (Node Black rx k v r)
|
||||
|
||||
Node _ lx kx vx rx ->
|
||||
Node Black (Node Red lx kx vx rx) k v r
|
||||
|
||||
balanceRight : Tree a b, a, b, Tree a b -> Tree a b
|
||||
balanceRight = \l, k, v, r ->
|
||||
when r is
|
||||
Leaf ->
|
||||
Leaf
|
||||
|
||||
Node _ (Node Red lx kx vx rx) ky vy ry ->
|
||||
Node Red (Node Black l k v lx) kx vx (Node Black rx ky vy ry)
|
||||
|
||||
Node _ lx kx vx (Node Red ly ky vy ry) ->
|
||||
Node Red (Node Black l k v lx) kx vx (Node Black ly ky vy ry)
|
||||
|
||||
Node _ lx kx vx rx ->
|
||||
Node Black l k v (Node Red lx kx vx rx)
|
||||
|
||||
isBlack : Color -> Bool
|
||||
isBlack = \c ->
|
||||
when c is
|
||||
Black -> Bool.true
|
||||
Red -> Bool.false
|
||||
|
||||
Del a b : [Del (Tree a b) Bool]
|
||||
|
||||
setRed : Map -> Map
|
||||
setRed = \t ->
|
||||
when t is
|
||||
Node _ l k v r ->
|
||||
Node Red l k v r
|
||||
|
||||
_ ->
|
||||
t
|
||||
|
||||
makeBlack : Map -> Del I64 Bool
|
||||
makeBlack = \t ->
|
||||
when t is
|
||||
Node Red l k v r ->
|
||||
Del (Node Black l k v r) Bool.false
|
||||
|
||||
_ ->
|
||||
Del t Bool.true
|
||||
|
||||
rebalanceLeft = \c, l, k, v, r ->
|
||||
when l is
|
||||
Node Black _ _ _ _ ->
|
||||
Del (balanceLeft (setRed l) k v r) (isBlack c)
|
||||
|
||||
Node Red lx kx vx rx ->
|
||||
Del (Node Black lx kx vx (balanceLeft (setRed rx) k v r)) Bool.false
|
||||
|
||||
_ ->
|
||||
boom "unreachable"
|
||||
|
||||
rebalanceRight = \c, l, k, v, r ->
|
||||
when r is
|
||||
Node Black _ _ _ _ ->
|
||||
Del (balanceRight l k v (setRed r)) (isBlack c)
|
||||
|
||||
Node Red lx kx vx rx ->
|
||||
Del (Node Black (balanceRight l k v (setRed lx)) kx vx rx) Bool.false
|
||||
|
||||
_ ->
|
||||
boom "unreachable"
|
||||
|
||||
delMin = \t ->
|
||||
when t is
|
||||
Node Black Leaf k v r ->
|
||||
when r is
|
||||
Leaf ->
|
||||
Delmin (Del Leaf Bool.true) k v
|
||||
|
||||
_ ->
|
||||
Delmin (Del (setBlack r) Bool.false) k v
|
||||
|
||||
Node Red Leaf k v r ->
|
||||
Delmin (Del r Bool.false) k v
|
||||
|
||||
Node c l k v r ->
|
||||
when delMin l is
|
||||
Delmin (Del lx Bool.true) kx vx ->
|
||||
Delmin (rebalanceRight c lx k v r) kx vx
|
||||
|
||||
Delmin (Del lx Bool.false) kx vx ->
|
||||
Delmin (Del (Node c lx k v r) Bool.false) kx vx
|
||||
|
||||
Leaf ->
|
||||
Delmin (Del t Bool.false) 0 Bool.false
|
||||
|
||||
delete : Tree I64 Bool, I64 -> Tree I64 Bool
|
||||
delete = \t, k ->
|
||||
when del t k is
|
||||
Del tx _ ->
|
||||
setBlack tx
|
||||
|
||||
del : Tree I64 Bool, I64 -> Del I64 Bool
|
||||
del = \t, k ->
|
||||
when t is
|
||||
Leaf ->
|
||||
Del Leaf Bool.false
|
||||
|
||||
Node cx lx kx vx rx ->
|
||||
if (k < kx) then
|
||||
when del lx k is
|
||||
Del ly Bool.true ->
|
||||
rebalanceRight cx ly kx vx rx
|
||||
|
||||
Del ly Bool.false ->
|
||||
Del (Node cx ly kx vx rx) Bool.false
|
||||
else if (k > kx) then
|
||||
when del rx k is
|
||||
Del ry Bool.true ->
|
||||
rebalanceLeft cx lx kx vx ry
|
||||
|
||||
Del ry Bool.false ->
|
||||
Del (Node cx lx kx vx ry) Bool.false
|
||||
else
|
||||
when rx is
|
||||
Leaf ->
|
||||
if isBlack cx then makeBlack lx else Del lx Bool.false
|
||||
|
||||
Node _ _ _ _ _ ->
|
||||
when delMin rx is
|
||||
Delmin (Del ry Bool.true) ky vy ->
|
||||
rebalanceLeft cx lx ky vy ry
|
||||
|
||||
Delmin (Del ry Bool.false) ky vy ->
|
||||
Del (Node cx lx ky vy ry) Bool.false
|
|
@ -3,7 +3,6 @@ app [main] { pf: platform "platform/main.roc" }
|
|||
import pf.PlatformTasks
|
||||
import AStar
|
||||
|
||||
#main : Task {} *
|
||||
main =
|
||||
PlatformTasks.putLine! (showBool test1)
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
|
||||
main =
|
||||
Stdout.line! "\nLet's count down from 3 together - all you have to do is press <ENTER>."
|
||||
_ = Stdin.line!
|
||||
Task.loop 3 tick
|
||||
|
||||
tick = \n ->
|
||||
if n == 0 then
|
||||
Stdout.line! "🎉 SURPRISE! Happy Birthday! 🎂"
|
||||
Task.ok (Done {})
|
||||
else
|
||||
Stdout.line! (n |> Num.toStr |> \s -> "$(s)...")
|
||||
_ = Stdin.line!
|
||||
Task.ok (Step (n - 1))
|
|
@ -1,35 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
|
||||
main =
|
||||
Stdout.line! "🗣 Shout into this cave and hear the echo! 👂👂👂"
|
||||
|
||||
Task.loop {} tick
|
||||
|
||||
tick : {} -> Task [Step {}, Done {}] _
|
||||
tick = \{} ->
|
||||
when Stdin.line |> Task.result! is
|
||||
Ok str -> Stdout.line (echo str) |> Task.map Step
|
||||
Err (StdinErr EndOfFile) -> Stdout.line (echo "Received end of input (EOF).") |> Task.map Done
|
||||
Err (StdinErr err) -> Stdout.line (echo "Unable to read input $(Inspect.toStr err)") |> Task.map Done
|
||||
|
||||
echo : Str -> Str
|
||||
echo = \shout ->
|
||||
silence = \length ->
|
||||
spaceInUtf8 = 32
|
||||
|
||||
List.repeat spaceInUtf8 length
|
||||
|
||||
shout
|
||||
|> Str.toUtf8
|
||||
|> List.mapWithIndex
|
||||
(\_, i ->
|
||||
length = (List.len (Str.toUtf8 shout) - i)
|
||||
phrase = (List.split (Str.toUtf8 shout) length).before
|
||||
|
||||
List.concat (silence (if i == 0 then 2 * length else length)) phrase)
|
||||
|> List.join
|
||||
|> Str.fromUtf8
|
||||
|> Result.withDefault ""
|
|
@ -1,30 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
import pf.Env
|
||||
|
||||
main =
|
||||
task =
|
||||
Env.decode "EDITOR"
|
||||
|> Task.await (\editor -> Stdout.line "Your favorite editor is $(editor)!")
|
||||
|> Task.await (\{} -> Env.decode "SHLVL")
|
||||
|> Task.await
|
||||
(\lvl ->
|
||||
when lvl is
|
||||
1u8 -> Stdout.line "You're running this in a root shell!"
|
||||
n ->
|
||||
lvlStr = Num.toStr n
|
||||
|
||||
Stdout.line "Your current shell level is $(lvlStr)!")
|
||||
|> Task.await \{} -> Env.decode "LETTERS"
|
||||
|
||||
Task.attempt task \result ->
|
||||
when result is
|
||||
Ok letters ->
|
||||
joinedLetters = Str.joinWith letters " "
|
||||
|
||||
Stdout.line "Your favorite letters are: $(joinedLetters)"
|
||||
|
||||
Err _ ->
|
||||
Stderr.line "I couldn't find your favorite letters in the environment variables!"
|
|
@ -1,36 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.File
|
||||
import pf.Path
|
||||
import pf.Env
|
||||
|
||||
main : Task {} [Exit I32 Str]_
|
||||
main =
|
||||
pathStr = "out.txt"
|
||||
|
||||
task =
|
||||
cwdPath = Env.cwd!
|
||||
cwdStr = Path.display cwdPath
|
||||
Stdout.line! "Current working directory: $(cwdStr)"
|
||||
|
||||
dirEntries = Path.listDir! cwdPath
|
||||
contentsStr = Str.joinWith (List.map dirEntries Path.display) "\n "
|
||||
Stdout.line! "Directory contents:\n $(contentsStr)\n"
|
||||
Stdout.line! "Writing a string to out.txt"
|
||||
File.writeUtf8! pathStr "a string!"
|
||||
contents = File.readUtf8! pathStr
|
||||
Stdout.line! "I read the file back. Its contents: \"$(contents)\""
|
||||
|
||||
when Task.result! task is
|
||||
Ok {} -> Stdout.line! "Successfully wrote a string to out.txt"
|
||||
Err err ->
|
||||
msg =
|
||||
when err is
|
||||
FileWriteErr _ PermissionDenied -> "PermissionDenied"
|
||||
FileWriteErr _ Unsupported -> "Unsupported"
|
||||
FileWriteErr _ (Unrecognized _ other) -> other
|
||||
FileReadErr _ _ -> "Error reading file"
|
||||
_ -> "Uh oh, there was an error!"
|
||||
|
||||
Task.err (Exit 1 msg)
|
|
@ -1,12 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
|
||||
main =
|
||||
Stdout.line! "What's your first name?"
|
||||
firstName = Stdin.line!
|
||||
Stdout.line! "What's your last name?"
|
||||
lastName = Stdin.line!
|
||||
|
||||
Stdout.line "Hi, $(firstName) $(lastName)! 👋"
|
|
@ -1,23 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Http
|
||||
import pf.Stdout
|
||||
|
||||
main =
|
||||
request = {
|
||||
method: Get,
|
||||
headers: [],
|
||||
url: "http://www.example.com",
|
||||
mimeType: "",
|
||||
body: [],
|
||||
timeout: TimeoutMilliseconds 5000,
|
||||
}
|
||||
|
||||
resp = Http.send! request
|
||||
|
||||
output =
|
||||
when resp |> Http.handleStringResponse is
|
||||
Err err -> crash (Http.errorToString err)
|
||||
Ok body -> body
|
||||
|
||||
Stdout.line output
|
|
@ -1,12 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import "test-file.txt" as testFile
|
||||
|
||||
main =
|
||||
# Due to the functions we apply on testFile, it will be inferred as a List U8.
|
||||
testFile
|
||||
|> List.map Num.toU64
|
||||
|> List.sum
|
||||
|> Num.toStr
|
||||
|> Stdout.line!
|
|
@ -1,12 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import "test-file.txt" as testFile : _ # the _ is optional
|
||||
|
||||
main =
|
||||
# Due to the functions we apply on testFile, it will be inferred as a List U8.
|
||||
testFile
|
||||
|> List.map Num.toU64
|
||||
|> List.sum
|
||||
|> Num.toStr
|
||||
|> Stdout.line!
|
|
@ -1,7 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import "ingested-file.roc" as ownCode : Str
|
||||
|
||||
main =
|
||||
Stdout.line! "\nThis roc file can print its own source code. The source is:\n\n$(ownCode)"
|
|
@ -1,85 +0,0 @@
|
|||
app [main] {
|
||||
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
|
||||
}
|
||||
|
||||
import pf.Stdout
|
||||
|
||||
main =
|
||||
file = strParam { name: "file" }
|
||||
argParser =
|
||||
{ cliBuild <-
|
||||
file,
|
||||
count: numParam { name: "count" },
|
||||
doubled: numParam { name: "doubled" }
|
||||
|> cliMap \d -> d * 2,
|
||||
}
|
||||
|
||||
args = ["parse-args", "file.txt", "5", "7"]
|
||||
when argParser |> parseArgs args is
|
||||
Ok data -> Stdout.line "Success: $(Inspect.toStr data)"
|
||||
Err (FailedToParse message) -> Stdout.line "Failed: $(message)"
|
||||
|
||||
ArgParseErr : [NoMoreArgs, InvalidParam ParamConfig]
|
||||
|
||||
ParamConfig : {
|
||||
name : Str,
|
||||
type : [Num, Str],
|
||||
}
|
||||
|
||||
ArgParser out : {
|
||||
params : List ParamConfig,
|
||||
parser : List Str -> Result (out, List Str) ArgParseErr,
|
||||
}
|
||||
|
||||
strParam : { name : Str } -> ArgParser Str
|
||||
strParam = \{ name } ->
|
||||
parser = \args ->
|
||||
when args is
|
||||
[] -> Err NoMoreArgs
|
||||
[first, .. as rest] -> Ok (first, rest)
|
||||
|
||||
{ params: [{ name, type: Str }], parser }
|
||||
|
||||
numParam : { name : Str } -> ArgParser U64
|
||||
numParam = \{ name } ->
|
||||
param = { name, type: Num }
|
||||
parser = \args ->
|
||||
when args is
|
||||
[] -> Err NoMoreArgs
|
||||
[first, .. as rest] ->
|
||||
when Str.toU64 first is
|
||||
Ok num -> Ok (num, rest)
|
||||
Err InvalidNumStr -> Err (InvalidParam param)
|
||||
|
||||
{ params: [param], parser }
|
||||
|
||||
cliMap : ArgParser a, (a -> b) -> ArgParser b
|
||||
cliMap = \{ params, parser }, mapper ->
|
||||
mappedParser = \args ->
|
||||
(data, afterData) = parser? args
|
||||
|
||||
Ok (mapper data, afterData)
|
||||
|
||||
{
|
||||
params,
|
||||
parser: mappedParser,
|
||||
}
|
||||
|
||||
cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c
|
||||
cliBuild = \firstWeaver, secondWeaver, combine ->
|
||||
allParams = List.concat firstWeaver.params secondWeaver.params
|
||||
combinedParser = \args ->
|
||||
(firstValue, afterFirst) = firstWeaver.parser? args
|
||||
(secondValue, afterSecond) = secondWeaver.parser? afterFirst
|
||||
|
||||
Ok (combine firstValue secondValue, afterSecond)
|
||||
|
||||
{ params: allParams, parser: combinedParser }
|
||||
|
||||
parseArgs : ArgParser a, List Str -> Result a [FailedToParse Str]
|
||||
parseArgs = \{ params: _, parser }, args ->
|
||||
when parser (List.dropFirst args 1) is
|
||||
Ok (data, []) -> Ok data
|
||||
Ok (_data, extraArgs) -> Err (FailedToParse "Got $(List.len extraArgs |> Inspect.toStr) extra args")
|
||||
Err NoMoreArgs -> Err (FailedToParse "I needed more args")
|
||||
Err (InvalidParam param) -> Err (FailedToParse "Parameter '$(param.name)' needed a $(Inspect.toStr param.type)")
|
|
@ -1,51 +0,0 @@
|
|||
app [main] {
|
||||
cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
|
||||
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
|
||||
}
|
||||
|
||||
import cli.Stdout
|
||||
import cli.Stderr
|
||||
import parser.Core exposing [Parser, buildPrimitiveParser, many]
|
||||
import parser.String exposing [parseStr]
|
||||
|
||||
main =
|
||||
lettersInput = "AAAiBByAABBwBtCCCiAyArBBx"
|
||||
ifLetterA = \l -> l == A
|
||||
when parseStr (many letterParser) lettersInput is
|
||||
Ok letters ->
|
||||
letters
|
||||
|> List.keepIf ifLetterA
|
||||
|> List.map \_ -> 1
|
||||
|> List.sum
|
||||
|> Num.toStr
|
||||
|> \countLetterA -> Stdout.line "I counted $(countLetterA) letter A's!"
|
||||
|
||||
Err _ -> Stderr.line "Ooops, something went wrong parsing letters"
|
||||
|
||||
Letter : [A, B, C, Other]
|
||||
|
||||
letterParser : Parser (List U8) Letter
|
||||
letterParser =
|
||||
buildPrimitiveParser \input ->
|
||||
valResult =
|
||||
when input is
|
||||
[] -> Err (ParsingFailure "Nothing to parse")
|
||||
['A', ..] -> Ok A
|
||||
['B', ..] -> Ok B
|
||||
['C', ..] -> Ok C
|
||||
_ -> Ok Other
|
||||
|
||||
valResult
|
||||
|> Result.map \val -> { val, input: List.dropFirst input 1 }
|
||||
|
||||
expect
|
||||
input = "B"
|
||||
parser = letterParser
|
||||
result = parseStr parser input
|
||||
result == Ok B
|
||||
|
||||
expect
|
||||
input = "BCXA"
|
||||
parser = many letterParser
|
||||
result = parseStr parser input
|
||||
result == Ok [B, C, Other, A]
|
|
@ -1,63 +0,0 @@
|
|||
app [main] {
|
||||
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
|
||||
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
|
||||
}
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
import parser.Core exposing [map, keep]
|
||||
import parser.String exposing [strFromUtf8]
|
||||
import parser.CSV
|
||||
|
||||
input : Str
|
||||
input = "Airplane!,1980,\"Robert Hays,Julie Hagerty\"\r\nCaddyshack,1980,\"Chevy Chase,Rodney Dangerfield,Ted Knight,Michael O'Keefe,Bill Murray\""
|
||||
|
||||
main =
|
||||
when CSV.parseStr movieInfoParser input is
|
||||
Ok movies ->
|
||||
moviesString =
|
||||
movies
|
||||
|> List.map movieInfoExplanation
|
||||
|> Str.joinWith ("\n")
|
||||
nMovies = List.len movies |> Num.toStr
|
||||
|
||||
Stdout.line "$(nMovies) movies were found:\n\n$(moviesString)\n\nParse success!\n"
|
||||
|
||||
Err problem ->
|
||||
when problem is
|
||||
ParsingFailure failure ->
|
||||
Stderr.line "Parsing failure: $(failure)\n"
|
||||
|
||||
ParsingIncomplete leftover ->
|
||||
leftoverStr = leftover |> List.map strFromUtf8 |> List.map (\val -> "\"$(val)\"") |> Str.joinWith ", "
|
||||
|
||||
Stderr.line "Parsing incomplete. Following leftover fields while parsing a record: $(leftoverStr)\n"
|
||||
|
||||
SyntaxError error ->
|
||||
Stderr.line "Parsing failure. Syntax error in the CSV: $(error)"
|
||||
|
||||
MovieInfo := { title : Str, releaseYear : U64, actors : List Str }
|
||||
|
||||
movieInfoParser =
|
||||
CSV.record (\title -> \releaseYear -> \actors -> @MovieInfo { title, releaseYear, actors })
|
||||
|> keep (CSV.field CSV.string)
|
||||
|> keep (CSV.field CSV.u64)
|
||||
|> keep (CSV.field actorsParser)
|
||||
|
||||
actorsParser =
|
||||
CSV.string
|
||||
|> map \val -> Str.split val ","
|
||||
|
||||
movieInfoExplanation = \@MovieInfo { title, releaseYear, actors } ->
|
||||
enumeratedActors = enumerate actors
|
||||
releaseYearStr = Num.toStr releaseYear
|
||||
|
||||
"The movie '$(title)' was released in $(releaseYearStr) and stars $(enumeratedActors)"
|
||||
|
||||
enumerate : List Str -> Str
|
||||
enumerate = \elements ->
|
||||
{ before: inits, others: last } = List.split elements (List.len elements - 1)
|
||||
|
||||
last
|
||||
|> List.prepend (inits |> Str.joinWith ", ")
|
||||
|> Str.joinWith " and "
|
|
@ -1 +0,0 @@
|
|||
Used by ingested-file-bytes.roc and ingested-file-bytes-no-ann.roc
|
File diff suppressed because it is too large
Load diff
1491
crates/cli/tests/cli_tests.rs
Normal file
1491
crates/cli/tests/cli_tests.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,9 +0,0 @@
|
|||
interface Transitive
|
||||
exposes [
|
||||
add,
|
||||
]
|
||||
imports []
|
||||
|
||||
add = \num1, num2 -> (num1 + num2)
|
||||
|
||||
expect add 1 2 == 3
|
|
@ -1,5 +0,0 @@
|
|||
package "transitive-tests"
|
||||
exposes [
|
||||
Direct,
|
||||
]
|
||||
packages {}
|
|
@ -1,4 +0,0 @@
|
|||
interface Dep1 exposes [str1] imports [Dep2]
|
||||
|
||||
str1 : Str
|
||||
str1 = Dep2.str2
|
|
@ -1,4 +0,0 @@
|
|||
interface Dep2 exposes [str2] imports []
|
||||
|
||||
str2 : Str
|
||||
str2 = "I am Dep2.str2"
|
|
@ -1,7 +0,0 @@
|
|||
app "multi-dep-str"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [Dep1]
|
||||
provides [main] to pf
|
||||
|
||||
main : Str
|
||||
main = Dep1.str1
|
|
@ -1,9 +0,0 @@
|
|||
platform "multi-module"
|
||||
requires {}{ main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
|
@ -1,7 +0,0 @@
|
|||
app "multi-dep-thunk"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [Dep1]
|
||||
provides [main] to pf
|
||||
|
||||
main : Str
|
||||
main = Dep1.value1 {}
|
|
@ -1,119 +0,0 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const str = @import("glue").str;
|
||||
const RocStr = str.RocStr;
|
||||
const testing = std.testing;
|
||||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
extern fn roc__mainForHost_1_exposed_generic(*RocStr) void;
|
||||
|
||||
const Align = 2 * @alignOf(usize);
|
||||
extern fn malloc(size: usize) callconv(.C) ?*anyopaque;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*anyopaque;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void;
|
||||
extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
|
||||
extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||
_ = alignment;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||
_ = old_size;
|
||||
_ = alignment;
|
||||
return realloc(@as([*]align(Align) u8, @alignCast(@ptrCast(c_ptr))), new_size);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
|
||||
_ = alignment;
|
||||
free(@as([*]align(Align) u8, @alignCast(@ptrCast(c_ptr))));
|
||||
}
|
||||
|
||||
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
|
||||
return memset(dst, value, size);
|
||||
}
|
||||
|
||||
export fn roc_panic(msg: *RocStr, tag_id: u32) callconv(.C) void {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
switch (tag_id) {
|
||||
0 => {
|
||||
stderr.print("Roc standard library crashed with message\n\n {s}\n\nShutting down\n", .{msg.asSlice()}) catch unreachable;
|
||||
},
|
||||
1 => {
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg.asSlice()}) catch unreachable;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
export fn roc_dbg(loc: *RocStr, msg: *RocStr, src: *RocStr) callconv(.C) void {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
stderr.print("[{s}] {s} = {s}\n", .{ loc.asSlice(), src.asSlice(), msg.asSlice() }) catch unreachable;
|
||||
}
|
||||
|
||||
extern fn kill(pid: c_int, sig: c_int) c_int;
|
||||
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
|
||||
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
|
||||
extern fn getppid() c_int;
|
||||
|
||||
fn roc_getppid() callconv(.C) c_int {
|
||||
return getppid();
|
||||
}
|
||||
|
||||
fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||
return shm_open(name, oflag, mode);
|
||||
}
|
||||
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
|
||||
return mmap(addr, length, prot, flags, fd, offset);
|
||||
}
|
||||
|
||||
comptime {
|
||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||
}
|
||||
|
||||
if (builtin.os.tag == .windows) {
|
||||
@export(roc_getppid_windows_stub, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||
}
|
||||
}
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub export fn main() i32 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
var timer = std.time.Timer.start() catch unreachable;
|
||||
|
||||
// actually call roc to populate the callresult
|
||||
var callresult = RocStr.empty();
|
||||
roc__mainForHost_1_exposed_generic(&callresult);
|
||||
|
||||
const nanos = timer.read();
|
||||
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
|
||||
|
||||
// stdout the result
|
||||
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
|
||||
|
||||
callresult.decref();
|
||||
|
||||
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn to_seconds(tms: std.os.timespec) f64 {
|
||||
return @as(f64, @floatFromInt(tms.tv_sec)) + (@as(f64, @floatFromInt(tms.tv_nsec)) / 1_000_000_000.0);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
platform "multi-dep-thunk"
|
||||
requires {}{ main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
6
crates/cli/tests/fixtures/packages/app.roc
vendored
6
crates/cli/tests/fixtures/packages/app.roc
vendored
|
@ -1,6 +0,0 @@
|
|||
app "packages-test"
|
||||
packages { pf: "platform/main.roc", json: "json/main.roc", csv: "csv/main.roc" }
|
||||
imports [json.JsonParser, csv.Csv]
|
||||
provides [main] to pf
|
||||
|
||||
main = "Hello, World! $(JsonParser.example) $(Csv.example)"
|
|
@ -1,6 +0,0 @@
|
|||
interface Csv
|
||||
exposes [example]
|
||||
imports []
|
||||
|
||||
example : Str
|
||||
example = "This text came from a CSV package!"
|
|
@ -1,6 +0,0 @@
|
|||
interface JsonParser
|
||||
exposes [example]
|
||||
imports []
|
||||
|
||||
example : Str
|
||||
example = "This text came from a package!"
|
|
@ -1,10 +0,0 @@
|
|||
app [main] {
|
||||
pf: platform "../packages/platform/main.roc",
|
||||
one: "one/main.roc",
|
||||
two: "two/main.roc",
|
||||
}
|
||||
|
||||
import one.One
|
||||
import two.Two
|
||||
|
||||
main = "$(One.example) | $(Two.example)"
|
|
@ -1,8 +0,0 @@
|
|||
app [main] {
|
||||
pf: platform "../packages/platform/main.roc",
|
||||
one: "one/main.roc",
|
||||
}
|
||||
|
||||
import one.One
|
||||
|
||||
main = One.example
|
|
@ -1,8 +0,0 @@
|
|||
app [main] {
|
||||
pf: platform "../packages/platform/main.roc",
|
||||
zero: "zero/main.roc",
|
||||
}
|
||||
|
||||
import zero.Zero
|
||||
|
||||
main = Zero.example
|
|
@ -1,3 +0,0 @@
|
|||
package [Zero] {
|
||||
one: "../one/main.roc"
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
interface ExposedNotDefined
|
||||
exposes [bar]
|
||||
imports []
|
|
@ -1,7 +0,0 @@
|
|||
interface UnusedImport
|
||||
exposes [plainText, emText]
|
||||
imports [Symbol.{ Ident }]
|
||||
|
||||
plainText = \str -> PlainText str
|
||||
|
||||
emText = \str -> EmText str
|
|
@ -1,7 +0,0 @@
|
|||
interface UnusedImportButWithALongFileNameForTesting
|
||||
exposes [plainText, emText]
|
||||
imports [Symbol.{ Ident }]
|
||||
|
||||
plainText = \str -> PlainText str
|
||||
|
||||
emText = \str -> EmText str
|
17
crates/cli/tests/markdown/form.md
Normal file
17
crates/cli/tests/markdown/form.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# A Simple Markdown Example
|
||||
|
||||
This file contains `form.roc` embedded as a block in Markdown. It lets us test that `roc check` works with Markdown.
|
||||
|
||||
```roc
|
||||
module [foo]
|
||||
|
||||
foo = "Foo"
|
||||
```
|
||||
|
||||
Excitingly, we can have another block of Roc code as well! (In this case it is the same one...)
|
||||
|
||||
```roc
|
||||
module [bar]
|
||||
|
||||
bar = "Bar"
|
||||
```
|
|
@ -1,8 +0,0 @@
|
|||
module {
|
||||
sendHttpReq,
|
||||
getEnvVar
|
||||
} -> [hi]
|
||||
|
||||
hi : Str
|
||||
hi =
|
||||
"hi"
|
|
@ -1,8 +0,0 @@
|
|||
app [main] {
|
||||
pf: platform "../fixtures/multi-dep-str/platform/main.roc",
|
||||
}
|
||||
|
||||
import BadAnn { appId: "one" }
|
||||
|
||||
main =
|
||||
""
|
|
@ -1,7 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import Menu { echo: Stdout.line }
|
||||
|
||||
main =
|
||||
Menu.menu "Agus"
|
|
@ -1,6 +0,0 @@
|
|||
app [main] {
|
||||
pf: platform "./platform/main.roc"
|
||||
}
|
||||
|
||||
main =
|
||||
"from app"
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_check_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
|
||||
── MISSING DEFINITION in tests/test-projects/known_bad/ExposedNotDefined.roc ───
|
||||
|
||||
bar is listed as exposed, but it isn't defined in this module.
|
||||
|
||||
You can fix this by adding a definition for bar, or by removing it
|
||||
from exposes.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
1 error and 0 warning found in <ignored for test> ms
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_check_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
|
||||
── TYPE MISMATCH in tests/test-projects/known_bad/TypeError.roc ────────────────
|
||||
|
||||
Something is off with the body of the main definition:
|
||||
|
||||
3│ main : Str -> Task {} []
|
||||
4│ main = \_ ->
|
||||
5│ "this is a string, not a Task {} [] function like the platform expects."
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The body is a string of type:
|
||||
|
||||
Str
|
||||
|
||||
But the type annotation on main says it should be:
|
||||
|
||||
Task {} []
|
||||
|
||||
Tip: Add type annotations to functions or values to help you figure
|
||||
this out.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
1 error and 0 warning found in <ignored for test> ms
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_check_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
|
||||
── UNUSED IMPORT in ...nown_bad/UnusedImportButWithALongFileNameForTesting.roc ─
|
||||
|
||||
Symbol is imported but not used.
|
||||
|
||||
3│ import Symbol exposing [Ident]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Since Symbol isn't used, you don't need to import it.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
0 error and 1 warning found in <ignored for test> ms
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_test_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
── UNRECOGNIZED PACKAGE in tests/test-projects/module_imports_pkg/Module.roc ───
|
||||
|
||||
This module is trying to import from `pkg`:
|
||||
|
||||
3│ import pkg.Foo
|
||||
^^^^^^^
|
||||
|
||||
A lowercase name indicates a package shorthand, but I don't know which
|
||||
packages are available.
|
||||
|
||||
When checking a module directly, I look for a `main.roc` app or
|
||||
package to resolve shorthands from.
|
||||
|
||||
You can create it, or specify an existing one with the --main flag.
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_test_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
── UNRECOGNIZED PACKAGE in ...rojects/module_imports_pkg/ImportsUnknownPkg.roc ─
|
||||
|
||||
This module is trying to import from `cli`:
|
||||
|
||||
3│ import cli.Foo
|
||||
^^^^^^^
|
||||
|
||||
A lowercase name indicates a package shorthand, but I don't recognize
|
||||
this one. Did you mean one of these?
|
||||
|
||||
pkg
|
||||
|
||||
Note: I'm using the following module to resolve package shorthands:
|
||||
|
||||
tests/test-projects/module_imports_pkg/app.roc
|
||||
|
||||
You can specify a different one with the --main flag.
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: out.normalize_stdout_and_stderr()
|
||||
---
|
||||
(@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\"})]})
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_dev_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
|
||||
|
||||
This expectation failed:
|
||||
|
||||
25│ expect words == []
|
||||
^^^^^^^^^^^
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
words : List Str
|
||||
words = ["this", "will", "for", "sure", "be", "a", "large", "string", "so", "when", "we", "split", "it", "it", "will", "use", "seamless", "slices", "which", "affect", "printing"]
|
||||
|
||||
Program finished!
|
||||
|
||||
[<ignored for tests>:28] x = 42
|
||||
[<ignored for tests>:30] "Fjoer en ferdjer frieten oan dyn geve lea" = "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||
[<ignored for tests>:32] "this is line 24" = "this is line 24"
|
||||
[<ignored for tests>:18] x = "abc"
|
||||
[<ignored for tests>:18] x = 10
|
||||
[<ignored for tests>:18] x = (A (B C))
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_test_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
|
||||
|
||||
This expectation failed:
|
||||
|
||||
6│ expect a == 2
|
||||
^^^^^^
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
a : Num *
|
||||
a = 1
|
||||
|
||||
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
|
||||
|
||||
This expectation failed:
|
||||
|
||||
7│ expect a == 3
|
||||
^^^^^^
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
a : Num *
|
||||
a = 1
|
||||
|
||||
── EXPECT FAILED in tests/test-projects/expects/expects.roc ────────────────────
|
||||
|
||||
This expectation failed:
|
||||
|
||||
11│> expect
|
||||
12│> a = makeA
|
||||
13│> b = 2i64
|
||||
14│>
|
||||
15│> a == b
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
a : Int Signed64
|
||||
a = 1
|
||||
|
||||
b : I64
|
||||
b = 2
|
||||
|
||||
|
||||
1 failed and 0 passed in <ignored for test> ms.
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_dev_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
App1.baseUrl: https://api.example.com/one
|
||||
App2.baseUrl: http://api.example.com/two
|
||||
App3.baseUrl: https://api.example.com/three
|
||||
App1.getUser 1: https://api.example.com/one/users/1
|
||||
App2.getUser 2: http://api.example.com/two/users/2
|
||||
App3.getUser 3: https://api.example.com/three/users/3
|
||||
App1.getPost 1: https://api.example.com/one/posts/1
|
||||
App2.getPost 2: http://api.example.com/two/posts/2
|
||||
App3.getPost 3: https://api.example.com/three/posts/3
|
||||
App1.getPosts [1, 2]: ["https://api.example.com/one/posts/1", "https://api.example.com/one/posts/2"]
|
||||
App2.getPosts [3, 4]: ["http://api.example.com/two/posts/3", "http://api.example.com/two/posts/4"]
|
||||
App2.getPosts [5, 6]: ["http://api.example.com/two/posts/5", "http://api.example.com/two/posts/6"]
|
||||
App1.getPostComments 1: https://api.example.com/one/posts/1/comments
|
||||
App2.getPostComments 2: http://api.example.com/two/posts/2/comments
|
||||
App2.getPostComments 3: http://api.example.com/two/posts/3/comments
|
||||
App1.getCompanies [1, 2]: ["https://api.example.com/one/companies/1", "https://api.example.com/one/companies/2"]
|
||||
App2.getCompanies [3, 4]: ["http://api.example.com/two/companies/3", "http://api.example.com/two/companies/4"]
|
||||
App2.getCompanies [5, 6]: ["http://api.example.com/two/companies/5", "http://api.example.com/two/companies/6"]
|
||||
App1.getPostAliased 1: https://api.example.com/one/posts/1
|
||||
App2.getPostAliased 2: http://api.example.com/two/posts/2
|
||||
App3.getPostAliased 3: https://api.example.com/three/posts/3
|
||||
App1.baseUrlAliased: https://api.example.com/one
|
||||
App2.baseUrlAliased: http://api.example.com/two
|
||||
App3.baseUrlAliased: https://api.example.com/three
|
||||
App1.getUserSafe 1: https://api.example.com/one/users/1
|
||||
Prod.getUserSafe 2: http://api.example.com/prod_1/users/2?safe=true
|
||||
usersApp1: ["https://api.example.com/one/users/1", "https://api.example.com/one/users/2", "https://api.example.com/one/users/3"]
|
||||
getUserApp3Nested 3: https://api.example.com/three/users/3
|
||||
usersApp3Passed: ["https://api.example.com/three/users/1", "https://api.example.com/three/users/2", "https://api.example.com/three/users/3"]
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
assertion_line: 429
|
||||
expression: cli_dev_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
|
||||
── TOO MANY ARGS in tests/test-projects/module_params/arity_mismatch.roc ───────
|
||||
|
||||
The getUser function expects 1 argument, but it got 2 instead:
|
||||
|
||||
12│ $(Api.getUser 1 2)
|
||||
^^^^^^^^^^^
|
||||
|
||||
Are there any missing commas? Or missing parentheses?
|
||||
|
||||
|
||||
── TOO MANY ARGS in tests/test-projects/module_params/arity_mismatch.roc ───────
|
||||
|
||||
This value is not a function, but it was given 1 argument:
|
||||
|
||||
13│ $(Api.baseUrl 1)
|
||||
^^^^^^^^^^^
|
||||
|
||||
Are there any missing commas? Or missing parentheses?
|
||||
|
||||
|
||||
── TOO FEW ARGS in tests/test-projects/module_params/arity_mismatch.roc ────────
|
||||
|
||||
The getPostComment function expects 2 arguments, but it got only 1:
|
||||
|
||||
16│ $(Api.getPostComment 1)
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Roc does not allow functions to be partially applied. Use a closure to
|
||||
make partial application explicit.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
3 error and 0 warning found in <ignored for test> ms
|
||||
.
|
||||
|
||||
You can run <ignored for tests>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
assertion_line: 445
|
||||
expression: cli_dev_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
|
||||
── TYPE MISMATCH in tests/test-projects/module_params/BadAnn.roc ───────────────
|
||||
|
||||
Something is off with the body of the fnAnnotatedAsValue definition:
|
||||
|
||||
3│ fnAnnotatedAsValue : Str
|
||||
4│> fnAnnotatedAsValue = \postId, commentId ->
|
||||
5│> "/posts/$(postId)/comments/$(Num.toStr commentId)"
|
||||
|
||||
The body is an anonymous function of type:
|
||||
|
||||
Str, Num * -> Str
|
||||
|
||||
But the type annotation on fnAnnotatedAsValue says it should be:
|
||||
|
||||
Str
|
||||
|
||||
|
||||
── TYPE MISMATCH in tests/test-projects/module_params/BadAnn.roc ───────────────
|
||||
|
||||
Something is off with the body of the missingArg definition:
|
||||
|
||||
7│ missingArg : Str -> Str
|
||||
8│> missingArg = \postId, _ ->
|
||||
9│> "/posts/$(postId)/comments"
|
||||
|
||||
The body is an anonymous function of type:
|
||||
|
||||
(Str, ? -> Str)
|
||||
|
||||
But the type annotation on missingArg says it should be:
|
||||
|
||||
(Str -> Str)
|
||||
|
||||
Tip: It looks like it takes too many arguments. I'm seeing 1 extra.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
2 error and 1 warning found in <ignored for test> ms
|
||||
.
|
||||
|
||||
You can run <ignored for tests>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
assertion_line: 476
|
||||
expression: cli_dev_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
|
||||
── TYPE MISMATCH in tests/test-projects/module_params/unexpected_fn.roc ────────
|
||||
|
||||
This argument to this string interpolation has an unexpected type:
|
||||
|
||||
11│ $(Api.getPost)
|
||||
^^^^^^^^^^^
|
||||
|
||||
The argument is an anonymous function of type:
|
||||
|
||||
U32 -> Str
|
||||
|
||||
But this string interpolation needs its argument to be:
|
||||
|
||||
Str
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
1 error and 0 warning found in <ignored for test> ms
|
||||
.
|
||||
|
||||
You can run <ignored for tests>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_test_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
Compiled in <ignored for test> ms.
|
||||
|
||||
Direct.roc:
|
||||
0 failed and 2 passed in <ignored for test> ms.
|
||||
|
||||
Transitive.roc:
|
||||
0 failed and 1 passed in <ignored for test> ms.
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
source: crates/cli/tests/cli_tests.rs
|
||||
expression: cli_check_out.normalize_stdout_and_stderr()
|
||||
---
|
||||
|
||||
── UNUSED IMPORT in tests/test-projects/known_bad/UnusedImport.roc ─────────────
|
||||
|
||||
Symbol is imported but not used.
|
||||
|
||||
3│ import Symbol exposing [Ident]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Since Symbol isn't used, you don't need to import it.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
0 error and 1 warning found in <ignored for test> ms
|
|
@ -110,18 +110,10 @@ comptime {
|
|||
pub export fn main() u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
var timer = std.time.Timer.start() catch unreachable;
|
||||
|
||||
const result = roc__mainForHost_1_exposed(10);
|
||||
|
||||
const nanos = timer.read();
|
||||
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
|
||||
|
||||
stdout.print("{d}\n", .{result}) catch unreachable;
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
app "fibonacci"
|
||||
packages { pf: "fibonacci-platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "fibonacci-platform/main.roc" }
|
||||
|
||||
main = \n -> fib n 0 1
|
||||
|
|
@ -127,8 +127,6 @@ pub export fn main() u8 {
|
|||
|
||||
var roc_list = RocList{ .elements = numbers, .length = NUM_NUMS, .capacity = NUM_NUMS };
|
||||
|
||||
var timer = std.time.Timer.start() catch unreachable;
|
||||
|
||||
// actually call roc to populate the callresult
|
||||
const callresult: RocList = roc__mainForHost_1_exposed(roc_list);
|
||||
|
||||
|
@ -136,9 +134,6 @@ pub export fn main() u8 {
|
|||
const length = @min(20, callresult.length);
|
||||
var result = callresult.elements[0..length];
|
||||
|
||||
const nanos = timer.read();
|
||||
const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0);
|
||||
|
||||
for (result, 0..) |x, i| {
|
||||
if (i == 0) {
|
||||
stdout.print("[{}, ", .{x}) catch unreachable;
|
||||
|
@ -149,12 +144,5 @@ pub export fn main() u8 {
|
|||
}
|
||||
}
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn to_seconds(tms: std.os.timespec) f64 {
|
||||
return @as(f64, @floatFromInt(tms.tv_sec)) + (@as(f64, @floatFromInt(tms.tv_nsec)) / 1_000_000_000.0);
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
app "quicksort"
|
||||
packages { pf: "quicksort-platform/main.roc" }
|
||||
imports []
|
||||
provides [quicksort] to pf
|
||||
app [quicksort] { pf: platform "quicksort-platform/main.roc" }
|
||||
|
||||
quicksort = \originalList ->
|
||||
n = List.len originalList
|
82
crates/cli/tests/test-projects/effectful/Community.roc
Normal file
82
crates/cli/tests/test-projects/effectful/Community.roc
Normal file
|
@ -0,0 +1,82 @@
|
|||
module [
|
||||
Community,
|
||||
empty,
|
||||
addPerson,
|
||||
addFriend,
|
||||
Person,
|
||||
walkFriendNames,
|
||||
]
|
||||
|
||||
## Datatype representing a community for demonstration purposes in inspect-gui.roc and inspect-logging.roc
|
||||
|
||||
Community := {
|
||||
people : List Person,
|
||||
friends : List (Set U64),
|
||||
}
|
||||
implements [Inspect]
|
||||
|
||||
Person := {
|
||||
firstName : Str,
|
||||
lastName : Str,
|
||||
age : U8,
|
||||
hasBeard : Bool,
|
||||
favoriteColor : Color,
|
||||
}
|
||||
implements [Inspect]
|
||||
|
||||
Color : [
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
RGB (U8, U8, U8),
|
||||
]
|
||||
|
||||
empty = @Community { people: [], friends: [] }
|
||||
|
||||
addPerson = \@Community { people, friends }, person ->
|
||||
@Community {
|
||||
people: List.append people (@Person person),
|
||||
friends: List.append friends (Set.empty {}),
|
||||
}
|
||||
|
||||
addFriend = \@Community { people, friends }, from, to ->
|
||||
when (List.get friends from, List.get friends to) is
|
||||
(Ok fromSet, Ok toSet) ->
|
||||
@Community {
|
||||
people,
|
||||
friends: friends
|
||||
|> List.set from (Set.insert fromSet to)
|
||||
|> List.set to (Set.insert toSet from),
|
||||
}
|
||||
|
||||
_ ->
|
||||
@Community { people, friends }
|
||||
|
||||
walkFriendNames : Community, state, (state, Str, Set Str -> state) -> state
|
||||
walkFriendNames = \@Community { people, friends }, s0, nextFn ->
|
||||
(out, _) =
|
||||
List.walk friends (s0, 0) \(s1, id), friendSet ->
|
||||
(@Person person) =
|
||||
when List.get people id is
|
||||
Ok v -> v
|
||||
Err _ -> crash "Unknown Person"
|
||||
personName =
|
||||
person.firstName
|
||||
|> Str.concat " "
|
||||
|> Str.concat person.lastName
|
||||
|
||||
friendNames =
|
||||
Set.walk friendSet (Set.empty {}) \friendsSet, friendId ->
|
||||
(@Person friend) =
|
||||
when List.get people friendId is
|
||||
Ok v -> v
|
||||
Err _ -> crash "Unknown Person"
|
||||
friendName =
|
||||
friend.firstName
|
||||
|> Str.concat " "
|
||||
|> Str.concat friend.lastName
|
||||
Set.insert friendsSet friendName
|
||||
|
||||
(nextFn s1 personName friendNames, id + 1)
|
||||
|
||||
out
|
|
@ -1,4 +1,4 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.16.0/O00IPk-Krg_diNS2dVWlI0ZQP794Vctxzv0ha96mK0E.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
|
35
crates/cli/tests/test-projects/effectful/echo.roc
Normal file
35
crates/cli/tests/test-projects/effectful/echo.roc
Normal file
|
@ -0,0 +1,35 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} -> tick! {}
|
||||
|
||||
tick! = \{} ->
|
||||
line = Effect.getLine! {}
|
||||
|
||||
if !(Str.isEmpty line) then
|
||||
Effect.putLine! (echo line)
|
||||
else
|
||||
Effect.putLine! "Received no input."
|
||||
|
||||
echo : Str -> Str
|
||||
echo = \shout ->
|
||||
silence = \length -> List.repeat ' ' length
|
||||
|
||||
shout
|
||||
|> Str.toUtf8
|
||||
|> List.mapWithIndex \_, i ->
|
||||
length = (List.len (Str.toUtf8 shout) - i)
|
||||
phrase = (List.splitAt (Str.toUtf8 shout) length).before
|
||||
|
||||
List.concat (silence (if i == 0 then 2 * length else length)) phrase
|
||||
|> List.join
|
||||
|> Str.fromUtf8
|
||||
|> Result.withDefault ""
|
||||
|
||||
expect
|
||||
message = "hello!"
|
||||
echoedMessage = echo message
|
||||
|
||||
echoedMessage == " hello! hello hell hel he h"
|
23
crates/cli/tests/test-projects/effectful/for_each_try.roc
Normal file
23
crates/cli/tests/test-projects/effectful/for_each_try.roc
Normal file
|
@ -0,0 +1,23 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
good = [0, 2, 4] |> List.forEachTry! validate!
|
||||
expect good == Ok {}
|
||||
|
||||
bad = [6, 8, 9, 10] |> List.forEachTry! validate!
|
||||
expect bad == Err 9
|
||||
|
||||
{}
|
||||
|
||||
validate! : U32 => Result {} U32
|
||||
validate! = \x ->
|
||||
if Num.isEven x then
|
||||
Effect.putLine! "✅ $(Num.toStr x)"
|
||||
Ok {}
|
||||
|
||||
else
|
||||
Effect.putLine! "$(Num.toStr x) is not even! ABORT!"
|
||||
Err x
|
27
crates/cli/tests/test-projects/effectful/form.roc
Normal file
27
crates/cli/tests/test-projects/effectful/form.roc
Normal file
|
@ -0,0 +1,27 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
first = ask! "What's your first name?"
|
||||
last = ask! "What's your last name?"
|
||||
|
||||
Effect.putLine! "\nHi, $(first) $(last)!\n"
|
||||
|
||||
when Str.toU8 (ask! "How old are you?") is
|
||||
Err InvalidNumStr ->
|
||||
Effect.putLine! "Enter a valid number"
|
||||
|
||||
Ok age if age >= 18 ->
|
||||
Effect.putLine! "\nNice! You can vote!"
|
||||
|
||||
Ok age ->
|
||||
Effect.putLine! "\nYou'll be able to vote in $(Num.toStr (18 - age)) years"
|
||||
|
||||
Effect.putLine! "\nBye! 👋"
|
||||
|
||||
ask! : Str => Str
|
||||
ask! = \question ->
|
||||
Effect.putLine! question
|
||||
Effect.getLine! {}
|
7
crates/cli/tests/test-projects/effectful/hello.roc
Normal file
7
crates/cli/tests/test-projects/effectful/hello.roc
Normal file
|
@ -0,0 +1,7 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
Effect.putLine! "I'm an effect 👻"
|
|
@ -0,0 +1,8 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
_ = Effect.getLine! {}
|
||||
Effect.putLine! "I asked for input and I ignored it. Deal with it! 😎"
|
35
crates/cli/tests/test-projects/effectful/inspect-logging.roc
Normal file
35
crates/cli/tests/test-projects/effectful/inspect-logging.roc
Normal file
|
@ -0,0 +1,35 @@
|
|||
#
|
||||
# Shows how Roc values can be logged
|
||||
#
|
||||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
import Community
|
||||
|
||||
main! = \{} ->
|
||||
Community.empty
|
||||
|> Community.addPerson {
|
||||
firstName: "John",
|
||||
lastName: "Smith",
|
||||
age: 27,
|
||||
hasBeard: Bool.true,
|
||||
favoriteColor: Blue,
|
||||
}
|
||||
|> Community.addPerson {
|
||||
firstName: "Debby",
|
||||
lastName: "Johnson",
|
||||
age: 47,
|
||||
hasBeard: Bool.false,
|
||||
favoriteColor: Green,
|
||||
}
|
||||
|> Community.addPerson {
|
||||
firstName: "Jane",
|
||||
lastName: "Doe",
|
||||
age: 33,
|
||||
hasBeard: Bool.false,
|
||||
favoriteColor: RGB (255, 255, 0),
|
||||
}
|
||||
|> Community.addFriend 0 2
|
||||
|> Community.addFriend 1 2
|
||||
|> Inspect.toStr
|
||||
|> Effect.putLine!
|
16
crates/cli/tests/test-projects/effectful/loops.roc
Normal file
16
crates/cli/tests/test-projects/effectful/loops.roc
Normal file
|
@ -0,0 +1,16 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
friends = ["Lu", "Marce", "Joaquin", "Chloé", "Mati", "Pedro"]
|
||||
printAll! friends
|
||||
|
||||
printAll! : List Str => {}
|
||||
printAll! = \friends ->
|
||||
when friends is
|
||||
[] -> {}
|
||||
[first, .. as remaining] ->
|
||||
Effect.putLine! first
|
||||
printAll! remaining
|
24
crates/cli/tests/test-projects/effectful/on_err.roc
Normal file
24
crates/cli/tests/test-projects/effectful/on_err.roc
Normal file
|
@ -0,0 +1,24 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
_ =
|
||||
authenticate! {}
|
||||
|> Result.onErr! \BadPass ->
|
||||
Effect.putLine! "LOG: Failed login attempt"
|
||||
Ok "Bad password"
|
||||
|
||||
{}
|
||||
|
||||
authenticate! : {} => Result Str [BadPass]
|
||||
authenticate! = \{} ->
|
||||
Effect.putLine! "Enter your password:"
|
||||
|
||||
password = Effect.getLine! {}
|
||||
|
||||
if password == "password" then
|
||||
Ok "You are in"
|
||||
else
|
||||
Err BadPass
|
19
crates/cli/tests/test-projects/effectful/print-line.roc
Normal file
19
crates/cli/tests/test-projects/effectful/print-line.roc
Normal file
|
@ -0,0 +1,19 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
["Welcome!", "What's your name?"]
|
||||
|> List.forEach! Effect.putLine!
|
||||
|
||||
line = Effect.getLine! {}
|
||||
|
||||
if line == "secret" then
|
||||
Effect.putLine! "You found the secret"
|
||||
Effect.putLine! "Congratulations!"
|
||||
else
|
||||
{}
|
||||
|
||||
Effect.putLine! "You entered: $(line)"
|
||||
Effect.putLine! "It is known"
|
|
@ -0,0 +1,22 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
Fx : {
|
||||
getLine!: {} => Str,
|
||||
}
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
notEffectful : Fx
|
||||
notEffectful = {
|
||||
getLine!: \{} -> "hardcoded"
|
||||
}
|
||||
|
||||
effectful : Fx
|
||||
effectful = {
|
||||
getLine!: Effect.getLine!
|
||||
}
|
||||
|
||||
Effect.putLine! "notEffectful: $(notEffectful.getLine! {})"
|
||||
Effect.putLine! "effectful: $(effectful.getLine! {})"
|
|
@ -0,0 +1,12 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
logged! "hello" (\{} -> Effect.putLine! "Hello, World!")
|
||||
|
||||
logged! = \name, fx! ->
|
||||
Effect.putLine! "Before $(name)"
|
||||
fx! {}
|
||||
Effect.putLine! "After $(name)"
|
|
@ -1,7 +1,4 @@
|
|||
app "expects-test"
|
||||
packages { pf: "zig-platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "../test-platform-simple-zig/main.roc" }
|
||||
|
||||
makeA =
|
||||
a = 1
|
||||
|
@ -24,7 +21,7 @@ polyDbg = \x ->
|
|||
|
||||
main =
|
||||
str = "this will for sure be a large string so when we split it it will use seamless slices which affect printing"
|
||||
words = Str.split str " "
|
||||
words = Str.splitOn str " "
|
||||
expect words == []
|
||||
|
||||
x = 42
|
|
@ -1,10 +1,8 @@
|
|||
interface Direct
|
||||
exposes [
|
||||
addAndStringify,
|
||||
]
|
||||
imports [
|
||||
Transitive,
|
||||
]
|
||||
module [
|
||||
addAndStringify,
|
||||
]
|
||||
|
||||
import Transitive
|
||||
|
||||
addAndStringify = \num1, num2 ->
|
||||
Num.toStr (Transitive.add num1 num2)
|
|
@ -0,0 +1,7 @@
|
|||
module [
|
||||
add,
|
||||
]
|
||||
|
||||
add = \num1, num2 -> (num1 + num2)
|
||||
|
||||
expect add 1 2 == 3
|
|
@ -0,0 +1,3 @@
|
|||
package [
|
||||
Direct,
|
||||
] {}
|
108
crates/cli/tests/test-projects/false-interpreter/Context.roc
Normal file
108
crates/cli/tests/test-projects/false-interpreter/Context.roc
Normal file
|
@ -0,0 +1,108 @@
|
|||
module [Context, Data, with, getChar, Option, pushStack, popStack, toStr, inWhileScope]
|
||||
|
||||
import pf.File
|
||||
import Variable exposing [Variable]
|
||||
|
||||
Option a : [Some a, None]
|
||||
|
||||
# The underlying context of the current location within the file
|
||||
Data : [Lambda (List U8), Number I32, Var Variable]
|
||||
# While loops are special and have their own Scope specific state.
|
||||
WhileState : { cond : List U8, body : List U8, state : [InCond, InBody] }
|
||||
Scope : { data : Option File.Handle, index : U64, buf : List U8, whileInfo : Option WhileState }
|
||||
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 }
|
||||
|
||||
pushStack : Context, Data -> Context
|
||||
pushStack = \ctx, data ->
|
||||
{ ctx & stack: List.append ctx.stack data }
|
||||
|
||||
# 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.
|
||||
# Otherwise it hits unreachable code in ir.rs
|
||||
popStack : Context -> Result [T Context Data] [EmptyStack]
|
||||
popStack = \ctx ->
|
||||
when List.last ctx.stack is
|
||||
Ok val ->
|
||||
poppedCtx = { ctx & stack: List.dropAt ctx.stack (List.len ctx.stack - 1) }
|
||||
|
||||
Ok (T poppedCtx val)
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Err EmptyStack
|
||||
|
||||
toStrData : Data -> Str
|
||||
toStrData = \data ->
|
||||
when data is
|
||||
Lambda _ -> "[]"
|
||||
Number n -> Num.toStr (Num.intCast n)
|
||||
Var v -> Variable.toStr v
|
||||
|
||||
toStrState : State -> Str
|
||||
toStrState = \state ->
|
||||
when state is
|
||||
Executing -> "Executing"
|
||||
InComment -> "InComment"
|
||||
InString _ -> "InString"
|
||||
InNumber _ -> "InNumber"
|
||||
InLambda _ _ -> "InLambda"
|
||||
InSpecialChar -> "InSpecialChar"
|
||||
LoadChar -> "LoadChar"
|
||||
|
||||
toStr : Context -> Str
|
||||
toStr = \{ scopes, stack, state, vars } ->
|
||||
depth = Num.toStr (List.len scopes)
|
||||
stateStr = toStrState state
|
||||
stackStr = Str.joinWith (List.map stack toStrData) " "
|
||||
varsStr = Str.joinWith (List.map vars toStrData) " "
|
||||
|
||||
"\n============\nDepth: $(depth)\nState: $(stateStr)\nStack: [$(stackStr)]\nVars: [$(varsStr)]\n============\n"
|
||||
|
||||
with : Str, (Context -> Task {} a) -> Task {} a
|
||||
with = \path, callback ->
|
||||
File.withOpen path \handle ->
|
||||
# I cant define scope here and put it in the list in callback. It breaks alias anaysis.
|
||||
# Instead I have to inline this.
|
||||
# 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 }
|
||||
|
||||
# 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 -> Task [T U8 Context] [EndOfData, NoScope]
|
||||
getChar = \ctx ->
|
||||
when List.last ctx.scopes is
|
||||
Ok scope ->
|
||||
(T val newScope) = getCharScope! scope
|
||||
Task.ok (T val { ctx & scopes: List.set ctx.scopes (List.len ctx.scopes - 1) newScope })
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Task.err NoScope
|
||||
|
||||
getCharScope : Scope -> Task [T U8 Scope] [EndOfData, NoScope]
|
||||
getCharScope = \scope ->
|
||||
when List.get scope.buf scope.index is
|
||||
Ok val ->
|
||||
Task.ok (T val { scope & index: scope.index + 1 })
|
||||
|
||||
Err OutOfBounds ->
|
||||
when scope.data is
|
||||
Some h ->
|
||||
bytes = File.chunk! h
|
||||
when List.first bytes is
|
||||
Ok val ->
|
||||
# This starts at 1 because the first character is already being returned.
|
||||
Task.ok (T val { scope & buf: bytes, index: 1 })
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Task.err EndOfData
|
||||
|
||||
None ->
|
||||
Task.err EndOfData
|
||||
|
||||
inWhileScope : Context -> Bool
|
||||
inWhileScope = \ctx ->
|
||||
when List.last ctx.scopes is
|
||||
Ok scope ->
|
||||
scope.whileInfo != None
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Bool.false
|
|
@ -0,0 +1,6 @@
|
|||
# False Interpreter
|
||||
|
||||
This is an interpreter for the [false programming language](https://strlen.com/false-language/).
|
||||
It is currently functional but runs in a way that devours stack space.
|
||||
There are many examples of applications in the examples sub folder.
|
||||
Many of them will currently cause stack overflows if stack size is not increased with something like `ulimit -s unlimited`.
|
|
@ -0,0 +1,34 @@
|
|||
module [Variable, fromUtf8, toIndex, totalCount, toStr]
|
||||
|
||||
# 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.
|
||||
Variable := U8
|
||||
|
||||
totalCount : U64
|
||||
totalCount =
|
||||
0x7A # "z"
|
||||
- 0x61 # "a"
|
||||
+ 1
|
||||
|
||||
toStr : Variable -> Str
|
||||
toStr = \@Variable char ->
|
||||
when Str.fromUtf8 [char] is
|
||||
Ok str -> str
|
||||
_ -> "_"
|
||||
|
||||
fromUtf8 : U8 -> Result Variable [InvalidVariableUtf8]
|
||||
fromUtf8 = \char ->
|
||||
if
|
||||
char
|
||||
>= 0x61 # "a"
|
||||
&& char
|
||||
<= 0x7A # "z"
|
||||
then
|
||||
Ok (@Variable char)
|
||||
else
|
||||
Err InvalidVariableUtf8
|
||||
|
||||
toIndex : Variable -> U64
|
||||
toIndex = \@Variable char ->
|
||||
Num.intCast (char - 0x61) # "a"
|
||||
# List.first (Str.toUtf8 "a")
|
|
@ -0,0 +1,5 @@
|
|||
{ False version of 99 Bottles by Marcus Comstedt (marcus@lysator.liu.se) }
|
||||
[$0=["no more bottles"]?$1=["One bottle"]?$1>[$." bottles"]?%" of beer"]b:
|
||||
100[$0>][$b;!" on the wall, "$b;!".
|
||||
"1-"Take one down, pass it around, "$b;!" on the wall.
|
||||
"]#%
|
|
@ -0,0 +1,224 @@
|
|||
{
|
||||
This should do the exact same thing as the linux cksum utility.
|
||||
It does a crc32 of the input data.
|
||||
One core difference, this reads off of stdin while cksum reads from a file.
|
||||
With the interpreter, it currently runs about 350x slower though and requires extended stack size.
|
||||
}
|
||||
{
|
||||
Load 256 constants from https://github.com/wertarbyte/coreutils/blob/f70c7b785b93dd436788d34827b209453157a6f2/src/cksum.c#L117
|
||||
To support the original false interpreter, numbers must be less than 32000.
|
||||
To deal with loading, just split all the numbers in two chunks.
|
||||
First chunk is lower 16 bits, second chunk is higher 16 bits shift to the right.
|
||||
Its values are then shifted back and merged together.
|
||||
}
|
||||
16564 45559 65536*| 23811 46390 65536*| 31706 47221 65536*| 26221 48308 65536*|
|
||||
13928 41715 65536*| 11231 42546 65536*| 3334 43889 65536*| 4273 44976 65536*|
|
||||
44300 38911 65536*| 45243 37694 65536*| 38498 40573 65536*| 35797 39612 65536*|
|
||||
56272 34043 65536*| 50791 32826 65536*| 57534 36217 65536*| 64777 35256 65536*|
|
||||
39876 64998 65536*| 34419 63783 65536*| 41130 62564 65536*| 48413 61605 65536*|
|
||||
60696 61154 65536*| 61615 59939 65536*| 54902 59232 65536*| 52161 58273 65536*|
|
||||
30332 56302 65536*| 27595 57135 65536*| 19730 53868 65536*| 20645 54957 65536*|
|
||||
160 51434 65536*| 7447 52267 65536*| 15310 49512 65536*| 9849 50601 65536*|
|
||||
63060 10708 65536*| 60387 11541 65536*| 52538 8278 65536*| 53389 9367 65536*|
|
||||
32904 15056 65536*| 40255 15889 65536*| 48102 13138 65536*| 42577 14227 65536*|
|
||||
7148 4060 65536*| 1627 2845 65536*| 8322 1630 65536*| 15669 671 65536*|
|
||||
27952 7384 65536*| 28807 6169 65536*| 22110 5466 65536*| 19433 4507 65536*|
|
||||
11556 26053 65536*| 12435 24836 65536*| 5706 27719 65536*| 3069 26758 65536*|
|
||||
23544 30401 65536*| 17999 29184 65536*| 24726 32579 65536*| 32033 31618 65536*|
|
||||
49308 17357 65536*| 56619 18188 65536*| 64498 19023 65536*| 58949 20110 65536*|
|
||||
46656 20681 65536*| 44023 21512 65536*| 36142 22859 65536*| 37017 23946 65536*|
|
||||
12483 34161 65536*| 11636 33200 65536*| 2989 36083 65536*| 5658 34866 65536*|
|
||||
17951 38517 65536*| 23464 37556 65536*| 32113 40951 65536*| 24774 39734 65536*|
|
||||
56699 41849 65536*| 49356 42936 65536*| 58901 43771 65536*| 64418 44602 65536*|
|
||||
43943 45181 65536*| 46608 46268 65536*| 37065 47615 65536*| 36222 48446 65536*|
|
||||
60339 51552 65536*| 62980 52641 65536*| 53469 49378 65536*| 52586 50211 65536*|
|
||||
40303 55908 65536*| 32984 56997 65536*| 42497 54246 65536*| 48054 55079 65536*|
|
||||
1547 61288 65536*| 7100 60329 65536*| 15717 59114 65536*| 8402 57899 65536*|
|
||||
28887 64620 65536*| 28000 63661 65536*| 19385 62958 65536*| 22030 61743 65536*|
|
||||
34339 7506 65536*| 39828 6547 65536*| 48461 5328 65536*| 41210 4113 65536*|
|
||||
61695 3670 65536*| 60744 2711 65536*| 52113 2004 65536*| 54822 789 65536*|
|
||||
27547 15194 65536*| 30252 16283 65536*| 20725 13016 65536*| 19778 13849 65536*|
|
||||
7495 10334 65536*| 240 11423 65536*| 9769 8668 65536*| 15262 9501 65536*|
|
||||
23891 20803 65536*| 16612 21890 65536*| 26173 22721 65536*| 31626 23552 65536*|
|
||||
11151 16967 65536*| 13880 18054 65536*| 4321 19397 65536*| 3414 20228 65536*|
|
||||
45291 30539 65536*| 44380 29578 65536*| 35717 32457 65536*| 38450 31240 65536*|
|
||||
50743 25679 65536*| 56192 24718 65536*| 64857 28109 65536*| 57582 26892 65536*|
|
||||
41050 55547 65536*| 48621 56378 65536*| 39732 53625 65536*| 34435 54712 65536*|
|
||||
54918 52223 65536*| 52017 53054 65536*| 60904 49789 65536*| 61535 50876 65536*|
|
||||
19938 65267 65536*| 20565 64050 65536*| 30348 63345 65536*| 27451 62384 65536*|
|
||||
15166 60919 65536*| 9865 59702 65536*| 80 58485 65536*| 7655 57524 65536*|
|
||||
31530 38122 65536*| 26269 36907 65536*| 16452 40296 65536*| 24051 39337 65536*|
|
||||
3574 34798 65536*| 4161 33583 65536*| 13976 36460 65536*| 11055 35501 65536*|
|
||||
38546 45794 65536*| 35621 46627 65536*| 44540 47968 65536*| 45131 49057 65536*|
|
||||
57422 41446 65536*| 65017 42279 65536*| 56096 43108 65536*| 50839 44197 65536*|
|
||||
5818 16600 65536*| 2829 17433 65536*| 11732 18778 65536*| 12387 19867 65536*|
|
||||
24678 21468 65536*| 32209 22301 65536*| 23304 23134 65536*| 18111 24223 65536*|
|
||||
64258 26320 65536*| 59061 25105 65536*| 49260 28498 65536*| 56795 27539 65536*|
|
||||
36318 30164 65536*| 36969 28949 65536*| 46768 31830 65536*| 43783 30871 65536*|
|
||||
52682 3273 65536*| 53373 2056 65536*| 63140 1355 65536*| 60179 394 65536*|
|
||||
47894 8141 65536*| 42657 6924 65536*| 32888 5711 65536*| 40399 4750 65536*|
|
||||
8306 10945 65536*| 15813 11776 65536*| 6940 9027 65536*| 1707 10114 65536*|
|
||||
22190 14789 65536*| 19225 15620 65536*| 28096 12359 65536*| 28791 13446 65536*|
|
||||
53293 60541 65536*| 52634 59580 65536*| 60227 58879 65536*| 63220 57662 65536*|
|
||||
42737 65401 65536*| 47942 64440 65536*| 40351 63227 65536*| 32808 62010 65536*|
|
||||
15765 51829 65536*| 8226 52916 65536*| 1787 50167 65536*| 6988 50998 65536*|
|
||||
19273 55665 65536*| 22270 56752 65536*| 28711 53491 65536*| 28048 54322 65536*|
|
||||
2909 41068 65536*| 5866 42157 65536*| 12339 43502 65536*| 11652 44335 65536*|
|
||||
32129 45928 65536*| 24630 47017 65536*| 18159 47850 65536*| 23384 48683 65536*|
|
||||
59109 34404 65536*| 64338 33445 65536*| 56715 36838 65536*| 49212 35623 65536*|
|
||||
36921 38240 65536*| 36238 37281 65536*| 43863 40162 65536*| 46816 38947 65536*|
|
||||
26317 29790 65536*| 31610 28831 65536*| 23971 32220 65536*| 16404 31005 65536*|
|
||||
4113 26458 65536*| 3494 25499 65536*| 11135 28376 65536*| 14024 27161 65536*|
|
||||
35701 21078 65536*| 38594 22167 65536*| 45083 23508 65536*| 44460 24341 65536*|
|
||||
64937 16722 65536*| 57374 17811 65536*| 50887 18640 65536*| 56176 19473 65536*|
|
||||
48573 14415 65536*| 40970 15502 65536*| 34515 12749 65536*| 39780 13580 65536*|
|
||||
52065 11083 65536*| 54998 12170 65536*| 61455 8905 65536*| 60856 9736 65536*|
|
||||
20485 7751 65536*| 19890 6790 65536*| 27499 6085 65536*| 30428 4868 65536*|
|
||||
9945 3395 65536*| 15214 2434 65536*| 7607 1217 65536*| 0
|
||||
|
||||
{load crc32 base 0}
|
||||
0
|
||||
|
||||
{load the xor function into x for use later}
|
||||
[
|
||||
{duplicate both inputs}
|
||||
1ø
|
||||
1ø
|
||||
|
||||
{nand inputs}
|
||||
&~
|
||||
|
||||
{bring original inputs to top of stack with rotation}
|
||||
@@
|
||||
|
||||
{or inputs}
|
||||
|
|
||||
|
||||
{and the nand and or result to get xor}
|
||||
&
|
||||
]x:
|
||||
|
||||
{load right shift function to r for later use}
|
||||
[
|
||||
{will right shift the second from top value by the top value}
|
||||
{while top value > 0}
|
||||
[$0>][
|
||||
{minus one from the top value}
|
||||
1-
|
||||
|
||||
{swap values}
|
||||
\
|
||||
|
||||
{divide the bottom value by 2}
|
||||
2/
|
||||
|
||||
{zero top bit to avoid sign extension}
|
||||
65535 32767 65536*|&
|
||||
|
||||
{swap back}
|
||||
\
|
||||
]#
|
||||
{drop the top value}
|
||||
%
|
||||
]r:
|
||||
|
||||
{i will be used to count the length. Set it to zero to start}
|
||||
0i:
|
||||
|
||||
{load the first character}
|
||||
^
|
||||
|
||||
{while data != - 1: # -1 is eof}
|
||||
[$1_=~][
|
||||
{increment i}
|
||||
i;1+i:
|
||||
|
||||
{duplicate crc32 which is on the stack under the current character}
|
||||
1ø
|
||||
|
||||
{Shift crc32 right by 24}
|
||||
24r;!
|
||||
|
||||
{xor the data and crc32}
|
||||
x;!
|
||||
|
||||
{and with 255 to ensure it is in range}
|
||||
255&
|
||||
|
||||
{
|
||||
The index goes into the constant array, but currently the crc32 is loaded infront of the constant array.
|
||||
Add 1 to the index to skip this and then load the value from the stack.
|
||||
}
|
||||
1+ø
|
||||
|
||||
{swap the crc32 on top of the stack}
|
||||
\
|
||||
|
||||
{left shift it by 8 (multiply by 0x100)}
|
||||
256*
|
||||
|
||||
{xor with the loaded constant}
|
||||
x;!
|
||||
|
||||
{load the next character}
|
||||
^
|
||||
]#
|
||||
|
||||
{drop the -1 left on top of the stack}
|
||||
%
|
||||
|
||||
{to match ck sum, add length to crc32}
|
||||
{load i}
|
||||
i;
|
||||
{Note, this will break if i is negative from overflow}
|
||||
{while i != 0}
|
||||
[$0=~][
|
||||
{duplicate and get last byte of i by and with 0xFF}
|
||||
$255&
|
||||
|
||||
{duplicate crc32 which is on the stack under i and the current byte}
|
||||
2ø
|
||||
|
||||
{Shift crc32 right by 24}
|
||||
24r;!
|
||||
|
||||
{xor the data and crc32}
|
||||
x;!
|
||||
|
||||
{and with 255 to ensure it is in range}
|
||||
255&
|
||||
|
||||
{
|
||||
The index goes into the constant array, but currently the i and the crc32 is loaded infront of the constant array.
|
||||
Add 1 to the index to skip this and then load the value from the stack.
|
||||
}
|
||||
2+ø
|
||||
|
||||
{rotate the crc32 on top of the stack}
|
||||
@
|
||||
|
||||
{left shift it by 8 (multiply by 0x100)}
|
||||
256*
|
||||
|
||||
{xor with the loaded constant}
|
||||
x;!
|
||||
|
||||
|
||||
|
||||
{swap i back on top of the stack and right shift it by 8}
|
||||
\8r;!
|
||||
]#
|
||||
{drop i}
|
||||
%
|
||||
|
||||
{ binary negate the crc32 }
|
||||
~
|
||||
|
||||
{print the crc32}
|
||||
.
|
||||
|
||||
{print a space}
|
||||
" "
|
||||
|
||||
{print the length}
|
||||
i;.
|
|
@ -0,0 +1,2 @@
|
|||
{ This will stack overflow if the input is too large }
|
||||
[^$1_=~][,]#
|
|
@ -0,0 +1,7 @@
|
|||
{ unix cksum, CRC32. -- Jonathan Neuschäfer <j.neuschaefer@gmx.net> }
|
||||
[[$0>][\2*\1-]#%]l:[0\128[$0>][$2O&0>[$@\64/x;!@@$g;*@x;!@@]?2/]#%%]h:
|
||||
[[$0>][\2/\1-]#%]r:[1O$8 28l;!1-&$@=~\24r;!\[128|]?x;!h;!\8l;!x;!]s:79764919g:
|
||||
[q1_0[\1+\^$1_>]s;#%@%\$@[1O0>][1O255&s;!\8r;!\]#~n;!32,%.10,]m:[$2O&@@|~|~]x:
|
||||
[$0\>\1O[$u;!]?\~[$.]?%]n:[h;y:[3+O]h:255[$0>][$y;!\1-]#m;!256[$0>][\%1-]#%]o:
|
||||
[1000$$**0@[$$0\>\4O\>~|][2O-\1+\]#\.\[10/$0>][\$2O/$.2O*-\]#%%]u: {width: 78}
|
||||
{ usage: run m for "main" or o for "optimized" (builds a lookup table) } o;!
|
|
@ -0,0 +1,11 @@
|
|||
{ This is a comment}
|
||||
|
||||
|
||||
{ White space doesn't matter}
|
||||
|
||||
|
||||
|
||||
{ Strings in False just automatically print...So, "Hello, World!", I guess }
|
||||
"Hello, World!
|
||||
"
|
||||
{ Note the new line created by the white space that matters in strings }
|
|
@ -0,0 +1 @@
|
|||
abc
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
Dijkstra's odd words Problem
|
||||
You'll need to enter a sentence in the Input box, ending with a period. Odd words are reversed.
|
||||
}
|
||||
|
||||
[[$' =][%^]#]b:
|
||||
[$$'.=\' =|~]w:
|
||||
[$'.=~[' ,]?]s:
|
||||
[w;![^o;!\,]?]o:
|
||||
^b;![$'.=~][w;[,^]#b;!s;!o;!b;!s;!]#,
|
|
@ -0,0 +1,2 @@
|
|||
{This prints all of the primes from 1 to 99}
|
||||
99 9[1-$][\$@$@$@$@\/*=[1-$$[%\1-$@]?0=[\$.' ,\]?]?]#
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue