mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 22:09:09 +00:00
Merge branch 'trunk' into rvcas/list_functions
This commit is contained in:
commit
dfcf7bb7a2
58 changed files with 1971 additions and 1318 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -40,6 +40,12 @@ jobs:
|
|||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache compiled valgrind
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/valgrind-3.6.1/
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: cargo fmt --check
|
||||
with:
|
||||
|
|
|
@ -59,7 +59,21 @@ esac
|
|||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
add-apt-repository "${REPO_NAME}"
|
||||
apt-get update
|
||||
apt-get install -y clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION libc++abi-dev libunwind-dev valgrind
|
||||
apt-get install -y clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION libc++abi-dev libunwind-dev libc6-dbg
|
||||
|
||||
wget https://sourceware.org/pub/valgrind/valgrind-3.16.1.tar.bz2
|
||||
tar -xf valgrind-3.16.1.tar.bz2
|
||||
mv valgrind-3.16.1 ~
|
||||
pushd ~/valgrind-3.16.1
|
||||
apt-get install -y autotools-dev automake
|
||||
./autogen.sh
|
||||
./configure
|
||||
make -j`nproc`
|
||||
sudo make install
|
||||
popd
|
||||
|
||||
# Report current valgrind version, to confirm it installed properly
|
||||
valgrind --version
|
||||
|
||||
# install zig - can't use apt-get since we require at least a specific commit later then the most recent tag (0.6.0)
|
||||
wget -c https://ziglang.org/builds/zig-linux-x86_64-0.6.0+0088efc4b.tar.xz --no-check-certificate
|
||||
|
|
|
@ -85,6 +85,9 @@ pub fn build_file(
|
|||
buf
|
||||
);
|
||||
|
||||
let cwd = app_o_file.parent().unwrap();
|
||||
let binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows
|
||||
|
||||
program::gen_from_mono_module(
|
||||
&arena,
|
||||
loaded,
|
||||
|
@ -106,11 +109,8 @@ pub fn build_file(
|
|||
size,
|
||||
);
|
||||
|
||||
let cwd = app_o_file.parent().unwrap();
|
||||
|
||||
// Step 2: link the precompiled host and compiled app
|
||||
let host_input_path = cwd.join("platform").join("host.o");
|
||||
let binary_path = cwd.join("app"); // TODO should be app.exe on Windows
|
||||
|
||||
// TODO we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get precompiled hosts from there.
|
||||
|
|
|
@ -109,7 +109,7 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
|||
Ok(ReplOutput::Problems(lines))
|
||||
} else {
|
||||
let context = Context::create();
|
||||
let module = arena.alloc(roc_gen::llvm::build::module_from_builtins(&context, "app"));
|
||||
let module = arena.alloc(roc_gen::llvm::build::module_from_builtins(&context, ""));
|
||||
let builder = context.create_builder();
|
||||
|
||||
// mark our zig-defined builtins as internal
|
||||
|
@ -274,7 +274,8 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
|||
}
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app Repl provides [ replOutput ] imports []\n\nreplOutput =\n");
|
||||
let mut buffer =
|
||||
String::from("app \"app\" provides [ replOutput ] to \"./platform\"\n\nreplOutput =\n");
|
||||
|
||||
for line in src.lines() {
|
||||
// indent the body!
|
||||
|
|
|
@ -17,7 +17,13 @@ mod cli_run {
|
|||
use serial_test::serial;
|
||||
use std::path::Path;
|
||||
|
||||
fn check_output(file: &Path, flags: &[&str], expected_ending: &str, use_valgrind: bool) {
|
||||
fn check_output(
|
||||
file: &Path,
|
||||
executable_filename: &str,
|
||||
flags: &[&str],
|
||||
expected_ending: &str,
|
||||
use_valgrind: bool,
|
||||
) {
|
||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat());
|
||||
if !compile_out.stderr.is_empty() {
|
||||
panic!(compile_out.stderr);
|
||||
|
@ -26,14 +32,31 @@ mod cli_run {
|
|||
|
||||
let out = if use_valgrind {
|
||||
let (valgrind_out, raw_xml) =
|
||||
run_with_valgrind(&[file.with_file_name("app").to_str().unwrap()]);
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml);
|
||||
run_with_valgrind(&[file.with_file_name(executable_filename).to_str().unwrap()]);
|
||||
|
||||
if valgrind_out.status.success() {
|
||||
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
||||
panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nvalgrind xml was: \"{}\"\n\nvalgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", err, raw_xml, valgrind_out.stdout, valgrind_out.stderr);
|
||||
});
|
||||
|
||||
if !memory_errors.is_empty() {
|
||||
panic!("{:?}", memory_errors);
|
||||
}
|
||||
} else {
|
||||
let exit_code = match valgrind_out.status.code() {
|
||||
Some(code) => format!("exit code {}", code),
|
||||
None => "no exit code".to_string(),
|
||||
};
|
||||
|
||||
panic!("`valgrind` exited with {}. valgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", exit_code, valgrind_out.stdout, valgrind_out.stderr);
|
||||
}
|
||||
|
||||
valgrind_out
|
||||
} else {
|
||||
run_cmd(file.with_file_name("app").to_str().unwrap(), &[])
|
||||
run_cmd(
|
||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||
&[],
|
||||
)
|
||||
};
|
||||
if !&out.stdout.ends_with(expected_ending) {
|
||||
panic!(
|
||||
|
@ -49,6 +72,7 @@ mod cli_run {
|
|||
fn run_hello_world() {
|
||||
check_output(
|
||||
&example_file("hello-world", "Hello.roc"),
|
||||
"hello-world",
|
||||
&[],
|
||||
"Hello, World!!!!!!!!!!!!!\n",
|
||||
true,
|
||||
|
@ -60,6 +84,7 @@ mod cli_run {
|
|||
fn run_hello_world_optimized() {
|
||||
check_output(
|
||||
&example_file("hello-world", "Hello.roc"),
|
||||
"hello-world",
|
||||
&[],
|
||||
"Hello, World!!!!!!!!!!!!!\n",
|
||||
true,
|
||||
|
@ -71,6 +96,7 @@ mod cli_run {
|
|||
fn run_quicksort_not_optimized() {
|
||||
check_output(
|
||||
&example_file("quicksort", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
|
@ -82,6 +108,7 @@ mod cli_run {
|
|||
fn run_quicksort_optimized() {
|
||||
check_output(
|
||||
&example_file("quicksort", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
|
@ -95,6 +122,7 @@ mod cli_run {
|
|||
fn run_quicksort_valgrind() {
|
||||
check_output(
|
||||
&example_file("quicksort", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
true,
|
||||
|
@ -108,6 +136,7 @@ mod cli_run {
|
|||
fn run_quicksort_optimized_valgrind() {
|
||||
check_output(
|
||||
&example_file("quicksort", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
true,
|
||||
|
@ -119,6 +148,7 @@ mod cli_run {
|
|||
fn run_multi_module() {
|
||||
check_output(
|
||||
&example_file("multi-module", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
|
@ -130,6 +160,7 @@ mod cli_run {
|
|||
fn run_multi_module_optimized() {
|
||||
check_output(
|
||||
&example_file("multi-module", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
false,
|
||||
|
@ -143,6 +174,7 @@ mod cli_run {
|
|||
fn run_multi_module_valgrind() {
|
||||
check_output(
|
||||
&example_file("multi-module", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&[],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
true,
|
||||
|
@ -156,6 +188,7 @@ mod cli_run {
|
|||
fn run_multi_module_optimized_valgrind() {
|
||||
check_output(
|
||||
&example_file("multi-module", "Quicksort.roc"),
|
||||
"quicksort",
|
||||
&["--optimize"],
|
||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||
true,
|
||||
|
@ -178,6 +211,7 @@ mod cli_run {
|
|||
fn run_multi_dep_str_unoptimized() {
|
||||
check_output(
|
||||
&fixture_file("multi-dep-str", "Main.roc"),
|
||||
"multi-dep-str",
|
||||
&[],
|
||||
"I am Dep2.str2\n",
|
||||
true,
|
||||
|
@ -189,6 +223,7 @@ mod cli_run {
|
|||
fn run_multi_dep_str_optimized() {
|
||||
check_output(
|
||||
&fixture_file("multi-dep-str", "Main.roc"),
|
||||
"multi-dep-str",
|
||||
&["--optimize"],
|
||||
"I am Dep2.str2\n",
|
||||
true,
|
||||
|
@ -200,6 +235,7 @@ mod cli_run {
|
|||
fn run_multi_dep_thunk_unoptimized() {
|
||||
check_output(
|
||||
&fixture_file("multi-dep-thunk", "Main.roc"),
|
||||
"multi-dep-thunk",
|
||||
&[],
|
||||
"I am Dep2.value2\n",
|
||||
true,
|
||||
|
@ -211,6 +247,7 @@ mod cli_run {
|
|||
fn run_multi_dep_thunk_optimized() {
|
||||
check_output(
|
||||
&fixture_file("multi-dep-thunk", "Main.roc"),
|
||||
"multi-dep-thunk",
|
||||
&["--optimize"],
|
||||
"I am Dep2.value2\n",
|
||||
true,
|
||||
|
|
5
cli/tests/fixtures/.gitignore
vendored
5
cli/tests/fixtures/.gitignore
vendored
|
@ -1,4 +1,3 @@
|
|||
app
|
||||
host.o
|
||||
c_host.o
|
||||
app.dSYM
|
||||
*.o
|
||||
*.dSYM
|
||||
|
|
1
cli/tests/fixtures/multi-dep-str/.gitignore
vendored
Normal file
1
cli/tests/fixtures/multi-dep-str/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
multi-dep-str
|
2
cli/tests/fixtures/multi-dep-str/Main.roc
vendored
2
cli/tests/fixtures/multi-dep-str/Main.roc
vendored
|
@ -1,4 +1,4 @@
|
|||
app Main provides [ main ] imports [ Dep1 ]
|
||||
app "multi-dep-str" imports [ Dep1 ] provides [ main ] to "./platform"
|
||||
|
||||
main : Str
|
||||
main = Dep1.str1
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
platform roc/quicksort
|
||||
provides []
|
||||
requires {}
|
||||
platform examples/multi-module
|
||||
requires { main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
effects Effect {}
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
|||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "Main_main_1_exposed"]
|
||||
#[link_name = "roc__main_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
|
1
cli/tests/fixtures/multi-dep-thunk/.gitignore
vendored
Normal file
1
cli/tests/fixtures/multi-dep-thunk/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
multi-dep-thunk
|
2
cli/tests/fixtures/multi-dep-thunk/Main.roc
vendored
2
cli/tests/fixtures/multi-dep-thunk/Main.roc
vendored
|
@ -1,4 +1,4 @@
|
|||
app Main provides [ main ] imports [ Dep1 ]
|
||||
app "multi-dep-thunk" imports [ Dep1 ] provides [ main ] to "./platform"
|
||||
|
||||
main : Str
|
||||
main = Dep1.value1 {}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
platform roc/quicksort
|
||||
provides []
|
||||
requires {}
|
||||
platform examples/multi-dep-thunk
|
||||
requires { main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
effects Effect {}
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
|||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "Main_main_1_exposed"]
|
||||
#[link_name = "roc__main_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
|
|
@ -161,18 +161,18 @@ pub struct ValgrindErrorXWhat {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn extract_valgrind_errors(xml: &str) -> Vec<ValgrindError> {
|
||||
let parsed_xml: ValgrindOutput =
|
||||
from_str(xml).unwrap_or_else(|err|
|
||||
panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nRaw valgrind output was:\n\n{}", err, xml));
|
||||
parsed_xml
|
||||
pub fn extract_valgrind_errors(xml: &str) -> Result<Vec<ValgrindError>, serde_xml_rs::Error> {
|
||||
let parsed_xml: ValgrindOutput = from_str(xml)?;
|
||||
let answer = parsed_xml
|
||||
.fields
|
||||
.iter()
|
||||
.filter_map(|field| match field {
|
||||
ValgrindField::Error(err) => Some(err.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
.collect();
|
||||
|
||||
Ok(answer)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::spaces::{fmt_spaces, INDENT};
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use roc_parse::ast::{
|
||||
AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, Module, PlatformHeader,
|
||||
};
|
||||
use roc_parse::ast::Module;
|
||||
use roc_parse::header::{AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PlatformHeader};
|
||||
use roc_region::all::Located;
|
||||
|
||||
pub fn fmt_module<'a>(buf: &mut String<'a>, module: &'a Module<'a>) {
|
||||
|
@ -113,7 +112,7 @@ fn fmt_imports<'a>(
|
|||
|
||||
fn fmt_exposes<'a>(
|
||||
buf: &mut String<'a>,
|
||||
loc_entries: &'a Vec<'a, Located<ExposesEntry<'a>>>,
|
||||
loc_entries: &'a Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.push('[');
|
||||
|
@ -137,11 +136,11 @@ fn fmt_exposes<'a>(
|
|||
buf.push(']');
|
||||
}
|
||||
|
||||
fn fmt_exposes_entry<'a>(buf: &mut String<'a>, entry: &'a ExposesEntry<'a>, indent: u16) {
|
||||
use roc_parse::ast::ExposesEntry::*;
|
||||
fn fmt_exposes_entry<'a>(buf: &mut String<'a>, entry: &'a ExposesEntry<'a, &'a str>, indent: u16) {
|
||||
use roc_parse::header::ExposesEntry::*;
|
||||
|
||||
match entry {
|
||||
Ident(ident) => buf.push_str(ident),
|
||||
Exposed(ident) => buf.push_str(ident),
|
||||
|
||||
SpaceBefore(sub_entry, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
|
@ -155,7 +154,7 @@ fn fmt_exposes_entry<'a>(buf: &mut String<'a>, entry: &'a ExposesEntry<'a>, inde
|
|||
}
|
||||
|
||||
fn fmt_imports_entry<'a>(buf: &mut String<'a>, entry: &'a ImportsEntry<'a>, indent: u16) {
|
||||
use roc_parse::ast::ImportsEntry::*;
|
||||
use roc_parse::header::ImportsEntry::*;
|
||||
|
||||
match entry {
|
||||
Module(module, loc_exposes_entries) => {
|
||||
|
@ -176,6 +175,10 @@ fn fmt_imports_entry<'a>(buf: &mut String<'a>, entry: &'a ImportsEntry<'a>, inde
|
|||
}
|
||||
}
|
||||
|
||||
Package(_name, _entries) => {
|
||||
todo!("TODO Format imported package");
|
||||
}
|
||||
|
||||
SpaceBefore(sub_entry, spaces) => {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
fmt_imports_entry(buf, sub_entry, indent);
|
||||
|
|
|
@ -1707,7 +1707,8 @@ fn expose_function_to_host<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
) {
|
||||
let c_function_name: String = format!("{}_exposed", roc_function.get_name().to_str().unwrap());
|
||||
let c_function_name: String =
|
||||
format!("roc_{}_exposed", roc_function.get_name().to_str().unwrap());
|
||||
|
||||
let result = expose_function_to_host_help(env, roc_function, &c_function_name);
|
||||
|
||||
|
|
|
@ -1186,7 +1186,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Quicksort provides [ main ] imports []
|
||||
app "quicksort" provides [ main ] to "./platform"
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
|
|
|
@ -535,7 +535,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app LinkedListLen0 provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
pi = 3.1415
|
||||
|
||||
|
@ -553,7 +553,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
|
@ -580,7 +580,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app LinkedListLenTwice0 provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
|
@ -607,7 +607,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
|
@ -634,7 +634,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
|
@ -661,7 +661,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
|
@ -689,7 +689,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
|
@ -717,7 +717,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
|
@ -744,7 +744,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
|
@ -907,7 +907,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
x = 42
|
||||
|
||||
|
@ -928,7 +928,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
foo = \{} ->
|
||||
x = 41
|
||||
|
@ -951,7 +951,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
foo = \{} ->
|
||||
x = 41
|
||||
|
@ -978,7 +978,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
foo = \{} ->
|
||||
x = 41
|
||||
|
@ -1006,7 +1006,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
|
||||
|
@ -1036,7 +1036,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
# succeed : a -> ({} -> a)
|
||||
succeed = \x -> \{} -> x
|
||||
|
@ -1063,7 +1063,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
|
||||
|
@ -1085,7 +1085,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
|
||||
|
@ -1110,7 +1110,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
|
@ -1144,7 +1144,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
|
@ -1175,7 +1175,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
State a : { count : Int, x : a }
|
||||
|
||||
|
@ -1202,7 +1202,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
|
@ -1284,7 +1284,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
|
@ -1323,7 +1323,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Dict k : [ Node k (Dict k) (Dict k), Empty ]
|
||||
|
||||
|
@ -1353,7 +1353,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
|
@ -1393,7 +1393,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
|
@ -1444,7 +1444,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
|
@ -1470,7 +1470,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
|
@ -1498,7 +1498,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
|
@ -1527,7 +1527,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
|
@ -1552,7 +1552,7 @@ mod gen_primitives {
|
|||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
BTree : [ Node BTree BTree, Leaf Int ]
|
||||
|
||||
|
|
|
@ -405,7 +405,7 @@ mod gen_records {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
f = \r ->
|
||||
when r is
|
||||
|
@ -455,7 +455,7 @@ mod gen_records {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
f = \r ->
|
||||
{ x ? 10, y } = r
|
||||
|
@ -492,7 +492,7 @@ mod gen_records {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
f = \r ->
|
||||
{ x ? 10, y } = r
|
||||
|
@ -512,7 +512,7 @@ mod gen_records {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
f = \r ->
|
||||
{ x ? 10, y } = r
|
||||
|
@ -565,7 +565,7 @@ mod gen_records {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
f = \{ x ? 10, y } -> x + y
|
||||
|
||||
|
|
|
@ -417,7 +417,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Maybe a : [ Just a, Nothing ]
|
||||
|
||||
|
@ -630,7 +630,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use roc_build::program::FunctionIterator;
|
|||
use roc_collections::all::{MutMap, MutSet};
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app Test provides [ main ] imports []\n\nmain =\n");
|
||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||
|
||||
for line in src.lines() {
|
||||
// indent the body!
|
||||
|
|
|
@ -19,9 +19,8 @@ use roc_mono::ir::{
|
|||
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
|
||||
};
|
||||
use roc_mono::layout::{Layout, LayoutCache};
|
||||
use roc_parse::ast::{
|
||||
self, Attempting, ExposesEntry, ImportsEntry, PlatformHeader, TypeAnnotation, TypedIdent,
|
||||
};
|
||||
use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation};
|
||||
use roc_parse::header::{ExposesEntry, ImportsEntry, PlatformHeader, TypedIdent};
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser::{self, Fail, Parser};
|
||||
use roc_region::all::{Located, Region};
|
||||
|
@ -40,6 +39,9 @@ use std::str::from_utf8_unchecked;
|
|||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
/// Default name for the binary generated for an app, if an invalid one was specified.
|
||||
const DEFAULT_APP_OUTPUT_PATH: &str = "app";
|
||||
|
||||
/// Filename extension for normal Roc modules
|
||||
const ROC_FILE_EXTENSION: &str = "roc";
|
||||
|
||||
|
@ -534,7 +536,7 @@ pub enum BuildProblem<'a> {
|
|||
#[derive(Debug)]
|
||||
struct ModuleHeader<'a> {
|
||||
module_id: ModuleId,
|
||||
module_name: ModuleName,
|
||||
module_name: AppOrInterfaceName<'a>,
|
||||
module_path: PathBuf,
|
||||
exposed_ident_ids: IdentIds,
|
||||
deps_by_name: MutMap<ModuleName, ModuleId>,
|
||||
|
@ -581,6 +583,7 @@ pub struct MonomorphizedModule<'a> {
|
|||
pub module_id: ModuleId,
|
||||
pub interns: Interns,
|
||||
pub subs: Subs,
|
||||
pub output_path: Box<str>,
|
||||
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||
pub mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
||||
|
@ -599,7 +602,7 @@ pub struct VariablySizedLayouts<'a> {
|
|||
#[derive(Debug)]
|
||||
struct ParsedModule<'a> {
|
||||
module_id: ModuleId,
|
||||
module_name: ModuleName,
|
||||
module_name: AppOrInterfaceName<'a>,
|
||||
module_path: PathBuf,
|
||||
src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
|
@ -618,7 +621,7 @@ enum Msg<'a> {
|
|||
CanonicalizedAndConstrained {
|
||||
constrained_module: ConstrainedModule,
|
||||
canonicalization_problems: Vec<roc_problem::can::Problem>,
|
||||
module_docs: ModuleDocumentation,
|
||||
module_docs: Option<ModuleDocumentation>,
|
||||
},
|
||||
MadeEffectModule {
|
||||
constrained_module: ConstrainedModule,
|
||||
|
@ -672,6 +675,7 @@ struct State<'a> {
|
|||
pub goal_phase: Phase,
|
||||
pub stdlib: StdLib,
|
||||
pub exposed_types: SubsByModule,
|
||||
pub output_path: Option<&'a str>,
|
||||
|
||||
pub headers_parsed: MutSet<ModuleId>,
|
||||
|
||||
|
@ -1243,6 +1247,7 @@ where
|
|||
root_id,
|
||||
goal_phase,
|
||||
stdlib,
|
||||
output_path: None,
|
||||
module_cache: ModuleCache::default(),
|
||||
dependencies: Dependencies::default(),
|
||||
procedures: MutMap::default(),
|
||||
|
@ -1427,6 +1432,22 @@ fn update<'a>(
|
|||
.sources
|
||||
.insert(parsed.module_id, (parsed.module_path.clone(), parsed.src));
|
||||
|
||||
// If this was an app module, set the output path to be
|
||||
// the module's declared "name".
|
||||
//
|
||||
// e.g. for `app "blah"` we should generate an output file named "blah"
|
||||
match &parsed.module_name {
|
||||
AppOrInterfaceName::App(output_str) => match output_str {
|
||||
StrLiteral::PlainLine(path) => {
|
||||
state.output_path = Some(path);
|
||||
}
|
||||
_ => {
|
||||
todo!("TODO gracefully handle a malformed string literal after `app` keyword.");
|
||||
}
|
||||
},
|
||||
AppOrInterfaceName::Interface(_) => {}
|
||||
}
|
||||
|
||||
let module_id = parsed.module_id;
|
||||
|
||||
state.module_cache.parsed.insert(parsed.module_id, parsed);
|
||||
|
@ -1450,10 +1471,9 @@ fn update<'a>(
|
|||
.can_problems
|
||||
.insert(module_id, canonicalization_problems);
|
||||
|
||||
state
|
||||
.module_cache
|
||||
.documentation
|
||||
.insert(module_id, module_docs);
|
||||
if let Some(docs) = module_docs {
|
||||
state.module_cache.documentation.insert(module_id, docs);
|
||||
}
|
||||
|
||||
state
|
||||
.module_cache
|
||||
|
@ -1751,6 +1771,7 @@ fn finish_specialization<'a>(
|
|||
let State {
|
||||
procedures,
|
||||
module_cache,
|
||||
output_path,
|
||||
..
|
||||
} = state;
|
||||
|
||||
|
@ -1771,6 +1792,7 @@ fn finish_specialization<'a>(
|
|||
can_problems,
|
||||
mono_problems,
|
||||
type_problems,
|
||||
output_path: output_path.unwrap_or(DEFAULT_APP_OUTPUT_PATH).into(),
|
||||
exposed_to_host,
|
||||
module_id: state.root_id,
|
||||
subs,
|
||||
|
@ -1967,7 +1989,10 @@ fn parse_header<'a>(
|
|||
|
||||
match parsed {
|
||||
Ok((ast::Module::Interface { header }, parse_state)) => Ok(send_header(
|
||||
header.name,
|
||||
Located {
|
||||
region: header.name.region,
|
||||
value: AppOrInterfaceName::Interface(header.name.value),
|
||||
},
|
||||
filename,
|
||||
header.exposes.into_bump_slice(),
|
||||
header.imports.into_bump_slice(),
|
||||
|
@ -1981,7 +2006,10 @@ fn parse_header<'a>(
|
|||
pkg_config_dir.pop();
|
||||
|
||||
let (module_id, app_module_header_msg) = send_header(
|
||||
header.name,
|
||||
Located {
|
||||
region: header.name.region,
|
||||
value: AppOrInterfaceName::App(header.name.value),
|
||||
},
|
||||
filename,
|
||||
header.provides.into_bump_slice(),
|
||||
header.imports.into_bump_slice(),
|
||||
|
@ -2083,22 +2111,36 @@ fn load_from_str<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AppOrInterfaceName<'a> {
|
||||
/// A filename
|
||||
App(StrLiteral<'a>),
|
||||
Interface(roc_parse::header::ModuleName<'a>),
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn send_header<'a>(
|
||||
name: Located<roc_parse::header::ModuleName<'a>>,
|
||||
loc_name: Located<AppOrInterfaceName<'a>>,
|
||||
filename: PathBuf,
|
||||
exposes: &'a [Located<ExposesEntry<'a>>],
|
||||
exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
imports: &'a [Located<ImportsEntry<'a>>],
|
||||
parse_state: parser::State<'a>,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
let declared_name: ModuleName = name.value.as_str().into();
|
||||
use AppOrInterfaceName::*;
|
||||
|
||||
// TODO check to see if declared_name is consistent with filename.
|
||||
let declared_name: ModuleName = match &loc_name.value {
|
||||
App(_) => ModuleName::APP.into(),
|
||||
Interface(module_name) => {
|
||||
// TODO check to see if module_name is consistent with filename.
|
||||
// If it isn't, report a problem!
|
||||
|
||||
module_name.as_str().into()
|
||||
}
|
||||
};
|
||||
|
||||
let mut imported: Vec<(ModuleName, Vec<Ident>, Region)> = Vec::with_capacity(imports.len());
|
||||
let mut imported_modules: MutSet<ModuleId> = MutSet::default();
|
||||
let mut scope_size = 0;
|
||||
|
@ -2200,15 +2242,13 @@ fn send_header<'a>(
|
|||
// We always need to send these, even if deps is empty,
|
||||
// because the coordinator thread needs to receive this message
|
||||
// to decrement its "pending" count.
|
||||
|
||||
// Send the header the main thread for processing,
|
||||
(
|
||||
home,
|
||||
Msg::Header(ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name: declared_name,
|
||||
module_name: loc_name.value,
|
||||
imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
|
@ -2619,8 +2659,14 @@ fn canonicalize_and_constrain<'a>(
|
|||
|
||||
// Generate documentation information
|
||||
// TODO: store timing information?
|
||||
let module_docs =
|
||||
crate::docs::generate_module_docs(module_name, &exposed_ident_ids, &parsed_defs);
|
||||
let module_docs = match module_name {
|
||||
AppOrInterfaceName::App(_) => None,
|
||||
AppOrInterfaceName::Interface(name) => Some(crate::docs::generate_module_docs(
|
||||
name.as_str().into(),
|
||||
&exposed_ident_ids,
|
||||
&parsed_defs,
|
||||
)),
|
||||
};
|
||||
|
||||
let mut var_store = VarStore::default();
|
||||
let canonicalized = canonicalize_module_defs(
|
||||
|
@ -2737,7 +2783,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
|
|||
}
|
||||
|
||||
fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) {
|
||||
use roc_parse::ast::ImportsEntry::*;
|
||||
use roc_parse::header::ImportsEntry::*;
|
||||
|
||||
match entry {
|
||||
Module(module_name, exposes) => {
|
||||
|
@ -2750,6 +2796,10 @@ fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) {
|
|||
(module_name.as_str().into(), exposed)
|
||||
}
|
||||
|
||||
Package(_package_name, _exposes) => {
|
||||
todo!("TODO support exposing package-qualified module names.");
|
||||
}
|
||||
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => {
|
||||
// Ignore spaces.
|
||||
exposed_from_import(*sub_entry)
|
||||
|
@ -2757,11 +2807,11 @@ fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) {
|
|||
}
|
||||
}
|
||||
|
||||
fn ident_from_exposed(entry: &ExposesEntry<'_>) -> Ident {
|
||||
use roc_parse::ast::ExposesEntry::*;
|
||||
fn ident_from_exposed(entry: &ExposesEntry<'_, &str>) -> Ident {
|
||||
use roc_parse::header::ExposesEntry::*;
|
||||
|
||||
match entry {
|
||||
Ident(ident) => (*ident).into(),
|
||||
Exposed(ident) => (*ident).into(),
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => ident_from_exposed(sub_entry),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
app Primary
|
||||
provides [ blah2, blah3, str, alwaysThree, identity, z, w, succeed, withDefault, yay ]
|
||||
app "primary"
|
||||
packages { blah: "./blah" }
|
||||
imports [ Dep1, Dep2.{ two, foo }, Dep3.Blah.{ bar }, Res ]
|
||||
provides [ blah2, blah3, str, alwaysThree, identity, z, w, succeed, withDefault, yay ] to blah
|
||||
|
||||
blah2 = Dep2.two
|
||||
blah3 = bar
|
||||
|
@ -12,7 +13,7 @@ alwaysThree = \_ -> "foo"
|
|||
|
||||
identity = \a -> a
|
||||
|
||||
z = identity (Primary.alwaysThree {})
|
||||
z = identity (alwaysThree {})
|
||||
|
||||
w : Dep1.Identity {}
|
||||
w = Identity {}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
app Quicksort
|
||||
provides [ swap, partition, partitionHelp, quicksort ]
|
||||
imports []
|
||||
app "quicksort" provides [ swap, partition, partitionHelp, quicksort ] to "./platform"
|
||||
|
||||
quicksort : List (Num a), Int, Int -> List (Num a)
|
||||
quicksort = \list, low, high ->
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app QuicksortOneDef provides [ quicksort ] imports []
|
||||
app "quicksort" provides [ quicksort ] to "./platform"
|
||||
|
||||
quicksort = \originalList ->
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
|
|
|
@ -23,6 +23,7 @@ mod test_load {
|
|||
use roc_collections::all::MutMap;
|
||||
use roc_constrain::module::SubsByModule;
|
||||
use roc_load::file::LoadedModule;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||
use roc_types::subs::Subs;
|
||||
|
@ -148,7 +149,10 @@ mod test_load {
|
|||
.get_name(loaded_module.module_id)
|
||||
.expect("Test ModuleID not found in module_ids");
|
||||
|
||||
// App module names are hardcoded and not based on anything user-specified
|
||||
if expected_name != ModuleName::APP {
|
||||
assert_eq!(expected_name, &InlinableString::from(module_name));
|
||||
}
|
||||
|
||||
loaded_module
|
||||
}
|
||||
|
@ -256,7 +260,10 @@ mod test_load {
|
|||
"Main",
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports [ RBTree ]
|
||||
app "test-app"
|
||||
packages { blah: "./blah" }
|
||||
imports [ RBTree ]
|
||||
provides [ main ] to blah
|
||||
|
||||
empty : RBTree.Dict Int Int
|
||||
empty = RBTree.empty
|
||||
|
|
|
@ -22,6 +22,7 @@ mod test_uniq_load {
|
|||
use roc_collections::all::MutMap;
|
||||
use roc_constrain::module::SubsByModule;
|
||||
use roc_load::file::LoadedModule;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||
use roc_types::subs::Subs;
|
||||
|
@ -66,7 +67,10 @@ mod test_uniq_load {
|
|||
.get_name(loaded_module.module_id)
|
||||
.expect("Test ModuleID not found in module_ids");
|
||||
|
||||
// App module names are hardcoded and not based on anything user-specified
|
||||
if expected_name != ModuleName::APP {
|
||||
assert_eq!(expected_name, &InlinableString::from(module_name));
|
||||
}
|
||||
|
||||
loaded_module
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ impl TagName {
|
|||
impl ModuleName {
|
||||
// NOTE: After adding one of these, go to `impl ModuleId` and
|
||||
// add a corresponding ModuleId to there!
|
||||
pub const APP: &'static str = ""; // app modules have no module name
|
||||
pub const BOOL: &'static str = "Bool";
|
||||
pub const STR: &'static str = "Str";
|
||||
pub const NUM: &'static str = "Num";
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,6 @@
|
|||
use crate::header::{ModuleName, PackageName};
|
||||
use crate::header::{AppHeader, ImportsEntry, InterfaceHeader, PlatformHeader, TypedIdent};
|
||||
use crate::ident::Ident;
|
||||
use bumpalo::collections::String;
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_module::operator::{BinOp, CalledVia, UnaryOp};
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
@ -13,20 +12,6 @@ pub enum Module<'a> {
|
|||
Platform { header: PlatformHeader<'a> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct InterfaceHeader<'a> {
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub after_interface_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WhenBranch<'a> {
|
||||
pub patterns: &'a [Loc<Pattern<'a>>],
|
||||
|
@ -34,94 +19,6 @@ pub struct WhenBranch<'a> {
|
|||
pub guard: Option<Loc<Expr<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AppHeader<'a> {
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub provides: Vec<'a, Loc<ExposesEntry<'a>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub after_app_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_provides: &'a [CommentOrNewline<'a>],
|
||||
pub after_provides: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub provides: Vec<'a, Loc<ExposesEntry<'a>>>,
|
||||
pub requires: Vec<'a, Loc<TypedIdent<'a>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub effects: Effects<'a>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub after_platform_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_provides: &'a [CommentOrNewline<'a>],
|
||||
pub after_provides: &'a [CommentOrNewline<'a>],
|
||||
pub before_requires: &'a [CommentOrNewline<'a>],
|
||||
pub after_requires: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Effects<'a> {
|
||||
pub spaces_before_effects_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub spaces_after_effects_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
|
||||
pub type_name: &'a str,
|
||||
pub entries: Vec<'a, Loc<TypedIdent<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TypedIdent<'a> {
|
||||
/// e.g.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
Entry {
|
||||
ident: Loc<&'a str>,
|
||||
spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||
ann: Loc<TypeAnnotation<'a>>,
|
||||
},
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ExposesEntry<'a> {
|
||||
/// e.g. `Task`
|
||||
Ident(&'a str),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a ExposesEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ExposesEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ImportsEntry<'a> {
|
||||
/// e.g. `Task` or `Task.{ Task, after }`
|
||||
Module(ModuleName<'a>, Vec<'a, Loc<ExposesEntry<'a>>>),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> ExposesEntry<'a> {
|
||||
pub fn as_str(&'a self) -> &'a str {
|
||||
use ExposesEntry::*;
|
||||
|
||||
match self {
|
||||
Ident(string) => string,
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => sub_entry.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WhenPattern<'a> {
|
||||
pub pattern: Loc<Pattern<'a>>,
|
||||
|
@ -633,15 +530,6 @@ impl<'a> Spaceable<'a> for TypeAnnotation<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for ExposesEntry<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ExposesEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ExposesEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for ImportsEntry<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ImportsEntry::SpaceBefore(self, spaces)
|
||||
|
|
|
@ -1730,17 +1730,8 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
};
|
||||
let region = loc_ident.region;
|
||||
let loc_pattern = Located { region, value };
|
||||
let (spaces_after_colon, state) = space0(min_indent).parse(arena, state)?;
|
||||
let (parsed_expr, state) =
|
||||
parse_def_signature(min_indent, colon_indent, arena, state, loc_pattern)?;
|
||||
|
||||
let answer = if spaces_after_colon.is_empty() {
|
||||
parsed_expr
|
||||
} else {
|
||||
Expr::SpaceBefore(arena.alloc(parsed_expr), spaces_after_colon)
|
||||
};
|
||||
|
||||
Ok((answer, state))
|
||||
parse_def_signature(min_indent, colon_indent, arena, state, loc_pattern)
|
||||
}
|
||||
(None, None) => {
|
||||
// We got nothin'
|
||||
|
@ -1977,17 +1968,8 @@ fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
Pattern::SpaceAfter(arena.alloc(pattern), spaces_before_colon)
|
||||
};
|
||||
let loc_pattern = Located { region, value };
|
||||
let (spaces_after_equals, state) = space0(min_indent).parse(arena, state)?;
|
||||
let (parsed_expr, state) =
|
||||
parse_def_signature(min_indent, colon_indent, arena, state, loc_pattern)?;
|
||||
|
||||
let answer = if spaces_after_equals.is_empty() {
|
||||
parsed_expr
|
||||
} else {
|
||||
Expr::SpaceBefore(arena.alloc(parsed_expr), spaces_after_equals)
|
||||
};
|
||||
|
||||
Ok((answer, state))
|
||||
parse_def_signature(min_indent, colon_indent, arena, state, loc_pattern)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,15 +1,43 @@
|
|||
use crate::ast::CommentOrNewline;
|
||||
use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
|
||||
use crate::blankspace::space0;
|
||||
use crate::ident::lowercase_ident;
|
||||
use crate::module::package_name;
|
||||
use crate::parser::{ascii_char, optional, Either, Parser};
|
||||
use crate::string_literal;
|
||||
use bumpalo::collections::Vec;
|
||||
use inlinable_string::InlinableString;
|
||||
use roc_region::all::Loc;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct PackageName<'a> {
|
||||
pub account: &'a str,
|
||||
pub pkg: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Version<'a> {
|
||||
Exact(&'a str),
|
||||
Range {
|
||||
min: &'a str,
|
||||
min_comparison: VersionComparison,
|
||||
max: &'a str,
|
||||
max_comparison: VersionComparison,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum VersionComparison {
|
||||
AllowsEqual,
|
||||
DisallowsEqual,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum PackageOrPath<'a> {
|
||||
Package(PackageName<'a>, Version<'a>),
|
||||
Path(StrLiteral<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ModuleName<'a>(&'a str);
|
||||
|
||||
impl<'a> Into<&'a str> for ModuleName<'a> {
|
||||
|
@ -34,46 +62,224 @@ impl<'a> ModuleName<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO is this all duplicated from parse::ast?
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct InterfaceHeader<'a> {
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub exposes: Vec<'a, Loc<Exposes<'a>>>,
|
||||
pub imports: Vec<'a, (ModuleName<'a>, Vec<'a, Loc<Imports<'a>>>)>,
|
||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub after_interface: &'a [CommentOrNewline<'a>],
|
||||
pub after_interface_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum To<'a> {
|
||||
ExistingPackage(&'a str),
|
||||
NewPackage(PackageOrPath<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AppHeader<'a> {
|
||||
pub imports: Vec<'a, (ModuleName<'a>, Loc<Imports<'a>>)>,
|
||||
pub name: Loc<StrLiteral<'a>>,
|
||||
pub packages: Vec<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub to: Loc<To<'a>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub after_app_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub after_packages: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
pub before_provides: &'a [CommentOrNewline<'a>],
|
||||
pub after_provides: &'a [CommentOrNewline<'a>],
|
||||
pub before_to: &'a [CommentOrNewline<'a>],
|
||||
pub after_to: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PackageHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageOrPath<'a>>)>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub after_package_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub after_packages: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Exposes<'a> {
|
||||
/// e.g. `Task`
|
||||
Ident(&'a str),
|
||||
pub struct PlatformHeader<'a> {
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub requires: Vec<'a, Loc<TypedIdent<'a>>>,
|
||||
pub exposes: Vec<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
pub packages: Vec<'a, Loc<PackageEntry<'a>>>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
|
||||
pub effects: Effects<'a>,
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a Exposes<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a Exposes<'a>, &'a [CommentOrNewline<'a>]),
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub after_platform_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_requires: &'a [CommentOrNewline<'a>],
|
||||
pub after_requires: &'a [CommentOrNewline<'a>],
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub after_packages: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
pub before_provides: &'a [CommentOrNewline<'a>],
|
||||
pub after_provides: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Imports<'a> {
|
||||
/// e.g. `Task` or `Task.{ Task, after }`
|
||||
Ident(&'a str, Vec<'a, &'a str>),
|
||||
pub struct Effects<'a> {
|
||||
pub spaces_before_effects_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub spaces_after_effects_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
|
||||
pub type_name: &'a str,
|
||||
pub entries: Vec<'a, Loc<TypedIdent<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ExposesEntry<'a, T> {
|
||||
/// e.g. `Task`
|
||||
Exposed(T),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a Imports<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a Imports<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceBefore(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a, T> Spaceable<'a> for ExposesEntry<'a, T> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ExposesEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
ExposesEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ImportsEntry<'a> {
|
||||
/// e.g. `Task` or `Task.{ Task, after }`
|
||||
Module(ModuleName<'a>, Vec<'a, Loc<ExposesEntry<'a, &'a str>>>),
|
||||
|
||||
/// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }`
|
||||
Package(&'a str, Vec<'a, Loc<&'a ImportsEntry<'a>>>),
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> ExposesEntry<'a, &'a str> {
|
||||
pub fn as_str(&'a self) -> &'a str {
|
||||
use ExposesEntry::*;
|
||||
|
||||
match self {
|
||||
Exposed(string) => string,
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => sub_entry.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TypedIdent<'a> {
|
||||
/// e.g.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
Entry {
|
||||
ident: Loc<&'a str>,
|
||||
spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||
ann: Loc<TypeAnnotation<'a>>,
|
||||
},
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PackageEntry<'a> {
|
||||
Entry {
|
||||
shorthand: &'a str,
|
||||
spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||
package_or_path: Loc<PackageOrPath<'a>>,
|
||||
},
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for PackageEntry<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PackageEntry::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
PackageEntry::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>> {
|
||||
move |arena, state| {
|
||||
// You may optionally have a package shorthand,
|
||||
// e.g. "uc" in `uc: roc/unicode 1.0.0`
|
||||
//
|
||||
// (Indirect dependencies don't have a shorthand.)
|
||||
let (opt_shorthand, state) = optional(and!(
|
||||
skip_second!(lowercase_ident(), ascii_char(b':')),
|
||||
space0(1)
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
let (package_or_path, state) = loc!(package_or_path()).parse(arena, state)?;
|
||||
let entry = match opt_shorthand {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry::Entry {
|
||||
shorthand,
|
||||
spaces_after_shorthand,
|
||||
package_or_path,
|
||||
},
|
||||
None => PackageEntry::Entry {
|
||||
shorthand: "",
|
||||
spaces_after_shorthand: &[],
|
||||
package_or_path,
|
||||
},
|
||||
};
|
||||
|
||||
Ok((entry, state))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>> {
|
||||
map!(
|
||||
either!(
|
||||
string_literal::parse(),
|
||||
and!(
|
||||
package_name(),
|
||||
skip_first!(one_or_more!(ascii_char(b' ')), package_version())
|
||||
)
|
||||
),
|
||||
|answer| {
|
||||
match answer {
|
||||
Either::First(str_literal) => PackageOrPath::Path(str_literal),
|
||||
Either::Second((name, version)) => PackageOrPath::Package(name, version),
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn package_version<'a>() -> impl Parser<'a, Version<'a>> {
|
||||
move |_, _| todo!("TODO parse package version")
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use crate::ast::{
|
||||
AppHeader, Attempting, CommentOrNewline, Def, Effects, ExposesEntry, ImportsEntry,
|
||||
InterfaceHeader, Module, PlatformHeader, TypedIdent,
|
||||
};
|
||||
use crate::ast::{Attempting, CommentOrNewline, Def, Module};
|
||||
use crate::blankspace::{space0, space0_around, space0_before, space1};
|
||||
use crate::expr::def;
|
||||
use crate::header::{ModuleName, PackageName};
|
||||
use crate::header::{
|
||||
package_entry, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry,
|
||||
InterfaceHeader, ModuleName, PackageEntry, PackageName, PackageOrPath, PlatformHeader, To,
|
||||
TypedIdent,
|
||||
};
|
||||
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
|
||||
use crate::parser::{
|
||||
self, ascii_char, ascii_string, loc, optional, peek_utf8_char, peek_utf8_char_at, unexpected,
|
||||
unexpected_eof, ParseResult, Parser, State,
|
||||
unexpected_eof, Either, ParseResult, Parser, State,
|
||||
};
|
||||
use crate::string_literal;
|
||||
use crate::type_annotation;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::Bump;
|
||||
|
@ -44,7 +46,7 @@ pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
|
|||
ascii_string("interface"),
|
||||
and!(space1(1), loc!(module_name()))
|
||||
),
|
||||
and!(exposes(), imports())
|
||||
and!(exposes_values(), imports())
|
||||
),
|
||||
|(
|
||||
(after_interface_keyword, name),
|
||||
|
@ -173,66 +175,114 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> {
|
||||
parser::map(
|
||||
pub fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
skip_first!(ascii_string("app"), and!(space1(1), loc!(module_name()))),
|
||||
and!(provides(), imports())
|
||||
skip_first!(
|
||||
ascii_string("app"),
|
||||
and!(space1(1), loc!(string_literal::parse()))
|
||||
),
|
||||
|(
|
||||
(after_app_keyword, name),
|
||||
and!(
|
||||
optional(packages()),
|
||||
and!(optional(imports()), provides_to())
|
||||
)
|
||||
),
|
||||
|arena, ((after_app_keyword, name), (opt_pkgs, (opt_imports, provides)))| {
|
||||
let (before_packages, after_packages, package_entries) = match opt_pkgs {
|
||||
Some(pkgs) => {
|
||||
let pkgs: Packages<'a> = pkgs; // rustc must be told the type here
|
||||
|
||||
(
|
||||
((before_provides, after_provides), provides),
|
||||
((before_imports, after_imports), imports),
|
||||
),
|
||||
)| {
|
||||
pkgs.before_packages_keyword,
|
||||
pkgs.after_packages_keyword,
|
||||
pkgs.entries,
|
||||
)
|
||||
}
|
||||
None => (&[] as _, &[] as _, Vec::new_in(arena)),
|
||||
};
|
||||
|
||||
// rustc must be told the type here
|
||||
#[allow(clippy::type_complexity)]
|
||||
let opt_imports: Option<(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ImportsEntry<'a>>>,
|
||||
)> = opt_imports;
|
||||
|
||||
let ((before_imports, after_imports), imports) =
|
||||
opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Vec::new_in(arena)));
|
||||
let provides: ProvidesTo<'a> = provides; // rustc must be told the type here
|
||||
|
||||
AppHeader {
|
||||
name,
|
||||
provides,
|
||||
packages: package_entries,
|
||||
imports,
|
||||
provides: provides.entries,
|
||||
to: provides.to,
|
||||
after_app_keyword,
|
||||
before_provides,
|
||||
after_provides,
|
||||
before_packages,
|
||||
after_packages,
|
||||
before_imports,
|
||||
after_imports,
|
||||
before_provides: provides.before_provides_keyword,
|
||||
after_provides: provides.after_provides_keyword,
|
||||
before_to: provides.before_to_keyword,
|
||||
after_to: provides.after_to_keyword,
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
||||
pub fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
||||
parser::map(
|
||||
and!(
|
||||
skip_first!(
|
||||
ascii_string("platform"),
|
||||
and!(space1(1), loc!(package_name()))
|
||||
),
|
||||
and!(provides(), and!(requires(), and!(imports(), effects())))
|
||||
and!(
|
||||
and!(
|
||||
and!(requires(), and!(exposes_modules(), packages())),
|
||||
and!(imports(), provides_without_to())
|
||||
),
|
||||
effects()
|
||||
)
|
||||
),
|
||||
|(
|
||||
(after_platform_keyword, name),
|
||||
(
|
||||
((before_provides, after_provides), provides),
|
||||
(
|
||||
(
|
||||
((before_requires, after_requires), requires),
|
||||
(((before_imports, after_imports), imports), effects),
|
||||
(((before_exposes, after_exposes), exposes), packages),
|
||||
),
|
||||
(
|
||||
((before_imports, after_imports), imports),
|
||||
((before_provides, after_provides), provides),
|
||||
),
|
||||
),
|
||||
effects,
|
||||
),
|
||||
)| {
|
||||
PlatformHeader {
|
||||
name,
|
||||
provides,
|
||||
requires,
|
||||
exposes,
|
||||
packages: packages.entries,
|
||||
imports,
|
||||
provides,
|
||||
effects,
|
||||
after_platform_keyword,
|
||||
before_provides,
|
||||
after_provides,
|
||||
before_requires,
|
||||
after_requires,
|
||||
before_exposes,
|
||||
after_exposes,
|
||||
before_packages: packages.before_packages_keyword,
|
||||
after_packages: packages.after_packages_keyword,
|
||||
before_imports,
|
||||
after_imports,
|
||||
before_provides,
|
||||
after_provides,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -243,19 +293,80 @@ pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>> {
|
|||
zero_or_more!(space0_around(loc(def(0)), 0))
|
||||
}
|
||||
|
||||
struct ProvidesTo<'a> {
|
||||
entries: Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
to: Located<To<'a>>,
|
||||
|
||||
before_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
before_to_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_to_keyword: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn provides<'a>() -> impl Parser<
|
||||
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
|
||||
and!(
|
||||
collection!(
|
||||
ascii_char(b'['),
|
||||
loc!(map!(unqualified_ident(), ExposesEntry::Exposed)),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
1
|
||||
),
|
||||
and!(
|
||||
space1(1),
|
||||
skip_first!(
|
||||
ascii_string("to"),
|
||||
and!(
|
||||
space1(1),
|
||||
loc!(either!(lowercase_ident(), package_or_path()))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
|(
|
||||
(before_provides_keyword, after_provides_keyword),
|
||||
(entries, (before_to_keyword, (after_to_keyword, loc_to))),
|
||||
)| {
|
||||
let loc_to: Located<Either<&'a str, PackageOrPath<'a>>> = loc_to;
|
||||
let to_val = match loc_to.value {
|
||||
Either::First(pkg) => To::ExistingPackage(pkg),
|
||||
Either::Second(pkg) => To::NewPackage(pkg),
|
||||
};
|
||||
let to = Located {
|
||||
value: to_val,
|
||||
region: loc_to.region,
|
||||
};
|
||||
|
||||
ProvidesTo {
|
||||
entries,
|
||||
to,
|
||||
before_provides_keyword,
|
||||
after_provides_keyword,
|
||||
before_to_keyword,
|
||||
after_to_keyword,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn provides_without_to<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a>>>,
|
||||
Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
),
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
|
||||
collection!(
|
||||
ascii_char(b'['),
|
||||
loc!(exposes_entry()),
|
||||
loc!(map!(unqualified_ident(), ExposesEntry::Exposed)),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
1
|
||||
|
@ -284,18 +395,18 @@ fn requires<'a>() -> impl Parser<
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn exposes<'a>() -> impl Parser<
|
||||
fn exposes_values<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a>>>,
|
||||
Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
),
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
|
||||
collection!(
|
||||
ascii_char(b'['),
|
||||
loc!(exposes_entry()),
|
||||
loc!(map!(unqualified_ident(), ExposesEntry::Exposed)),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
1
|
||||
|
@ -303,6 +414,56 @@ fn exposes<'a>() -> impl Parser<
|
|||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn exposes_modules<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
),
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
|
||||
collection!(
|
||||
ascii_char(b'['),
|
||||
loc!(map!(module_name(), ExposesEntry::Exposed)),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
struct Packages<'a> {
|
||||
entries: Vec<'a, Located<PackageEntry<'a>>>,
|
||||
|
||||
before_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn packages<'a>() -> impl Parser<'a, Packages<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("packages")), space1(1)),
|
||||
collection!(
|
||||
ascii_char(b'{'),
|
||||
loc!(package_entry()),
|
||||
ascii_char(b','),
|
||||
ascii_char(b'}'),
|
||||
1
|
||||
)
|
||||
),
|
||||
|((before_packages_keyword, after_packages_keyword), entries)| {
|
||||
Packages {
|
||||
entries,
|
||||
before_packages_keyword,
|
||||
after_packages_keyword,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn imports<'a>() -> impl Parser<
|
||||
'a,
|
||||
|
@ -382,11 +543,6 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn exposes_entry<'a>() -> impl Parser<'a, ExposesEntry<'a>> {
|
||||
map!(unqualified_ident(), ExposesEntry::Ident)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
|
||||
map_with_arena!(
|
||||
|
@ -398,7 +554,7 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
|
|||
ascii_char(b'.'),
|
||||
collection!(
|
||||
ascii_char(b'{'),
|
||||
loc!(exposes_entry()),
|
||||
loc!(map!(unqualified_ident(), ExposesEntry::Exposed)),
|
||||
ascii_char(b','),
|
||||
ascii_char(b'}'),
|
||||
1
|
||||
|
@ -408,7 +564,7 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
|
|||
|arena,
|
||||
(module_name, opt_values): (
|
||||
ModuleName<'a>,
|
||||
Option<Vec<'a, Located<ExposesEntry<'a>>>>
|
||||
Option<Vec<'a, Located<ExposesEntry<'a, &'a str>>>>
|
||||
)| {
|
||||
let exposed_values = opt_values.unwrap_or_else(|| Vec::new_in(arena));
|
||||
|
||||
|
|
|
@ -1037,7 +1037,11 @@ macro_rules! one_or_more {
|
|||
}
|
||||
}
|
||||
}
|
||||
Err((_, new_state)) => Err(unexpected_eof(0, new_state.attempting, new_state)),
|
||||
Err((_, new_state)) => Err($crate::parser::unexpected_eof(
|
||||
0,
|
||||
new_state.attempting,
|
||||
new_state,
|
||||
)),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1083,9 +1087,9 @@ macro_rules! either {
|
|||
let original_attempting = state.attempting;
|
||||
|
||||
match $p1.parse(arena, state) {
|
||||
Ok((output, state)) => Ok((Either::First(output), state)),
|
||||
Ok((output, state)) => Ok(($crate::parser::Either::First(output), state)),
|
||||
Err((_, state)) => match $p2.parse(arena, state) {
|
||||
Ok((output, state)) => Ok((Either::Second(output), state)),
|
||||
Ok((output, state)) => Ok(($crate::parser::Either::Second(output), state)),
|
||||
Err((fail, state)) => Err((
|
||||
Fail {
|
||||
attempting: original_attempting,
|
||||
|
|
|
@ -12,10 +12,10 @@ pub fn parse_expr_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<
|
|||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_header_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Module<'a>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
let answer = header().parse(arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
|
|
|
@ -21,13 +21,16 @@ mod test_parse {
|
|||
use roc_parse::ast::CommentOrNewline::*;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::Pattern::{self, *};
|
||||
use roc_parse::ast::StrLiteral::*;
|
||||
use roc_parse::ast::StrLiteral::{self, *};
|
||||
use roc_parse::ast::StrSegment::*;
|
||||
use roc_parse::ast::{
|
||||
self, Attempting, Def, EscapedChar, InterfaceHeader, Spaceable, TypeAnnotation, WhenBranch,
|
||||
self, Attempting, Def, EscapedChar, Spaceable, TypeAnnotation, WhenBranch,
|
||||
};
|
||||
use roc_parse::header::ModuleName;
|
||||
use roc_parse::module::{interface_header, module_defs};
|
||||
use roc_parse::header::{
|
||||
AppHeader, Effects, ExposesEntry, InterfaceHeader, ModuleName, PackageEntry, PackageName,
|
||||
PackageOrPath, PlatformHeader, To,
|
||||
};
|
||||
use roc_parse::module::{app_header, interface_header, module_defs, platform_header};
|
||||
use roc_parse::parser::{Fail, FailReason, Parser, State};
|
||||
use roc_parse::test_helpers::parse_expr_with;
|
||||
use roc_region::all::{Located, Region};
|
||||
|
@ -1733,6 +1736,45 @@ mod test_parse {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_type_signature() {
|
||||
assert_parses_to(
|
||||
"f :\n {}\n\n42",
|
||||
Defs(
|
||||
&[&Located::new(
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
6,
|
||||
Def::Annotation(
|
||||
Located::new(0, 0, 0, 1, Pattern::Identifier("f")),
|
||||
Located::new(
|
||||
1,
|
||||
1,
|
||||
4,
|
||||
6,
|
||||
TypeAnnotation::SpaceBefore(
|
||||
&TypeAnnotation::Record {
|
||||
fields: &[],
|
||||
ext: None,
|
||||
final_comments: &[],
|
||||
},
|
||||
&[Newline],
|
||||
),
|
||||
),
|
||||
),
|
||||
)],
|
||||
&Located::new(
|
||||
3,
|
||||
3,
|
||||
0,
|
||||
2,
|
||||
Expr::SpaceBefore(&Expr::Num("42"), &[Newline, Newline]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn type_signature_function_def() {
|
||||
// use TypeAnnotation;
|
||||
|
@ -2200,7 +2242,237 @@ mod test_parse {
|
|||
// MODULE
|
||||
|
||||
#[test]
|
||||
fn empty_module() {
|
||||
fn empty_app_header() {
|
||||
let arena = Bump::new();
|
||||
let packages = Vec::new_in(&arena);
|
||||
let imports = Vec::new_in(&arena);
|
||||
let provides = Vec::new_in(&arena);
|
||||
let module_name = StrLiteral::PlainLine("test-app");
|
||||
let expected = AppHeader {
|
||||
name: Located::new(0, 0, 4, 14, module_name),
|
||||
packages,
|
||||
imports,
|
||||
provides,
|
||||
to: Located::new(0, 0, 53, 57, To::ExistingPackage("blah")),
|
||||
after_app_keyword: &[],
|
||||
before_packages: &[],
|
||||
after_packages: &[],
|
||||
before_imports: &[],
|
||||
after_imports: &[],
|
||||
before_provides: &[],
|
||||
after_provides: &[],
|
||||
before_to: &[],
|
||||
after_to: &[],
|
||||
};
|
||||
|
||||
let src = indoc!(
|
||||
r#"
|
||||
app "test-app" packages {} imports [] provides [] to blah
|
||||
"#
|
||||
);
|
||||
let actual = app_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimal_app_header() {
|
||||
use PackageOrPath::Path;
|
||||
|
||||
let arena = Bump::new();
|
||||
let packages = Vec::new_in(&arena);
|
||||
let imports = Vec::new_in(&arena);
|
||||
let provides = Vec::new_in(&arena);
|
||||
let module_name = StrLiteral::PlainLine("test-app");
|
||||
let expected = AppHeader {
|
||||
name: Located::new(0, 0, 4, 14, module_name),
|
||||
packages,
|
||||
imports,
|
||||
provides,
|
||||
to: Located::new(0, 0, 30, 38, To::NewPackage(Path(PlainLine("./blah")))),
|
||||
after_app_keyword: &[],
|
||||
before_packages: &[],
|
||||
after_packages: &[],
|
||||
before_imports: &[],
|
||||
after_imports: &[],
|
||||
before_provides: &[],
|
||||
after_provides: &[],
|
||||
before_to: &[],
|
||||
after_to: &[],
|
||||
};
|
||||
|
||||
let src = indoc!(
|
||||
r#"
|
||||
app "test-app" provides [] to "./blah"
|
||||
"#
|
||||
);
|
||||
let actual = app_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_app_header() {
|
||||
use ExposesEntry::Exposed;
|
||||
use PackageOrPath::Path;
|
||||
|
||||
let pkg_entry = PackageEntry::Entry {
|
||||
shorthand: "base",
|
||||
spaces_after_shorthand: &[],
|
||||
package_or_path: Located::new(0, 0, 33, 45, Path(PlainLine("./platform"))),
|
||||
};
|
||||
let loc_pkg_entry = Located::new(0, 0, 27, 45, pkg_entry);
|
||||
let arena = Bump::new();
|
||||
let packages = bumpalo::vec![in &arena; loc_pkg_entry];
|
||||
let imports = Vec::new_in(&arena);
|
||||
let provide_entry = Located::new(0, 0, 59, 68, Exposed("quicksort"));
|
||||
let provides = bumpalo::vec![in &arena; provide_entry];
|
||||
let module_name = StrLiteral::PlainLine("quicksort");
|
||||
let expected = AppHeader {
|
||||
name: Located::new(0, 0, 4, 15, module_name),
|
||||
packages,
|
||||
imports,
|
||||
provides,
|
||||
to: Located::new(0, 0, 74, 78, To::ExistingPackage("base")),
|
||||
after_app_keyword: &[],
|
||||
before_packages: &[],
|
||||
after_packages: &[],
|
||||
before_imports: &[],
|
||||
after_imports: &[],
|
||||
before_provides: &[],
|
||||
after_provides: &[],
|
||||
before_to: &[],
|
||||
after_to: &[],
|
||||
};
|
||||
|
||||
let src = indoc!(
|
||||
r#"
|
||||
app "quicksort" packages { base: "./platform" } provides [ quicksort ] to base
|
||||
"#
|
||||
);
|
||||
let actual = app_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_platform_header() {
|
||||
let pkg_name = PackageName {
|
||||
account: "rtfeldman",
|
||||
pkg: "blah",
|
||||
};
|
||||
let arena = Bump::new();
|
||||
let effects = Effects {
|
||||
type_name: "Blah",
|
||||
entries: Vec::new_in(&arena),
|
||||
spaces_before_effects_keyword: &[],
|
||||
spaces_after_effects_keyword: &[],
|
||||
spaces_after_type_name: &[],
|
||||
};
|
||||
let expected = PlatformHeader {
|
||||
name: Located::new(0, 0, 9, 23, pkg_name),
|
||||
requires: Vec::new_in(&arena),
|
||||
exposes: Vec::new_in(&arena),
|
||||
packages: Vec::new_in(&arena),
|
||||
imports: Vec::new_in(&arena),
|
||||
provides: Vec::new_in(&arena),
|
||||
effects,
|
||||
after_platform_keyword: &[],
|
||||
before_requires: &[],
|
||||
after_requires: &[],
|
||||
before_exposes: &[],
|
||||
after_exposes: &[],
|
||||
before_packages: &[],
|
||||
after_packages: &[],
|
||||
before_imports: &[],
|
||||
after_imports: &[],
|
||||
before_provides: &[],
|
||||
after_provides: &[],
|
||||
};
|
||||
|
||||
let src = "platform rtfeldman/blah requires {} exposes [] packages {} imports [] provides [] effects Blah {}";
|
||||
let actual = platform_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonempty_platform_header() {
|
||||
use ExposesEntry::Exposed;
|
||||
use PackageOrPath::Path;
|
||||
|
||||
let newlines = &[Newline];
|
||||
let pkg_name = PackageName {
|
||||
account: "foo",
|
||||
pkg: "barbaz",
|
||||
};
|
||||
let pkg_entry = PackageEntry::Entry {
|
||||
shorthand: "foo",
|
||||
spaces_after_shorthand: &[],
|
||||
package_or_path: Located::new(3, 3, 20, 27, Path(PlainLine("./foo"))),
|
||||
};
|
||||
let loc_pkg_entry = Located::new(3, 3, 15, 27, pkg_entry);
|
||||
let arena = Bump::new();
|
||||
let packages = bumpalo::vec![in &arena; loc_pkg_entry];
|
||||
let imports = Vec::new_in(&arena);
|
||||
let provide_entry = Located::new(5, 5, 15, 26, Exposed("mainForHost"));
|
||||
let provides = bumpalo::vec![in &arena; provide_entry];
|
||||
let effects = Effects {
|
||||
type_name: "Effect",
|
||||
entries: Vec::new_in(&arena),
|
||||
spaces_before_effects_keyword: newlines,
|
||||
spaces_after_effects_keyword: &[],
|
||||
spaces_after_type_name: &[],
|
||||
};
|
||||
let expected = PlatformHeader {
|
||||
name: Located::new(0, 0, 9, 19, pkg_name),
|
||||
requires: Vec::new_in(&arena),
|
||||
exposes: Vec::new_in(&arena),
|
||||
packages,
|
||||
imports,
|
||||
provides,
|
||||
effects,
|
||||
after_platform_keyword: &[],
|
||||
before_requires: newlines,
|
||||
after_requires: &[],
|
||||
before_exposes: newlines,
|
||||
after_exposes: &[],
|
||||
before_packages: newlines,
|
||||
after_packages: &[],
|
||||
before_imports: newlines,
|
||||
after_imports: &[],
|
||||
before_provides: newlines,
|
||||
after_provides: &[],
|
||||
};
|
||||
|
||||
let src = indoc!(
|
||||
r#"
|
||||
platform foo/barbaz
|
||||
requires {}
|
||||
exposes []
|
||||
packages { foo: "./foo" }
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects Effect {}
|
||||
"#
|
||||
);
|
||||
let actual = platform_header()
|
||||
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
|
||||
.map(|tuple| tuple.0);
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_interface_header() {
|
||||
let arena = Bump::new();
|
||||
let exposes = Vec::new_in(&arena);
|
||||
let imports = Vec::new_in(&arena);
|
||||
|
|
|
@ -128,7 +128,8 @@ mod solve_expr {
|
|||
}
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app Test provides [ main ] imports []\n\nmain =\n");
|
||||
let mut buffer =
|
||||
String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||
|
||||
for line in src.lines() {
|
||||
// indent the body!
|
||||
|
@ -2054,7 +2055,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Peano : [ S Peano, Z ]
|
||||
|
||||
|
@ -2138,7 +2139,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
|
@ -2194,7 +2195,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
map =
|
||||
\peano ->
|
||||
|
@ -2632,7 +2633,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
boom = \_ -> boom {}
|
||||
|
||||
|
@ -2978,7 +2979,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
|
||||
main : List x
|
||||
|
@ -2998,7 +2999,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
|
||||
main =
|
||||
|
@ -3019,7 +3020,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Bar : [ Bar ]
|
||||
Foo : [ Foo Bar Int, Empty ]
|
||||
|
@ -3045,7 +3046,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Foo : [ @Foo [ @Bar ] Int, @Empty ]
|
||||
|
||||
|
@ -3070,7 +3071,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
State a : { count : Int, x : a }
|
||||
|
||||
|
@ -3095,7 +3096,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
# The color of a node. Leaves are considered Black.
|
||||
NodeColor : [ Red, Black ]
|
||||
|
@ -3128,7 +3129,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Dict k : [ Node k (Dict k), Empty ]
|
||||
|
||||
|
@ -3153,7 +3154,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
|
@ -3383,7 +3384,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Dict k : [ Node k (Dict k) (Dict k), Empty ]
|
||||
|
||||
|
@ -3423,7 +3424,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
|
@ -3499,7 +3500,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ partitionHelp ] imports []
|
||||
app "test" provides [ partitionHelp ] to "./platform"
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
|
@ -3537,7 +3538,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Dict k : [ Node k (Dict k) (Dict k), Empty ]
|
||||
|
||||
|
@ -3559,7 +3560,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Dict k : [ Node k (Dict k) (Dict k), Empty ]
|
||||
|
||||
|
@ -3583,7 +3584,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
|
|
6
examples/.gitignore
vendored
6
examples/.gitignore
vendored
|
@ -1,5 +1,3 @@
|
|||
app
|
||||
host.o
|
||||
c_host.o
|
||||
roc_app.o
|
||||
app.dSYM
|
||||
*.o
|
||||
*.dSYM
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app Closure provides [ makeClosure ] imports []
|
||||
app "closure" provides [ makeClosure ] to "./platform/"
|
||||
|
||||
makeClosure : ({} -> Int) as MyClosure
|
||||
makeClosure =
|
||||
|
@ -6,4 +6,3 @@ makeClosure =
|
|||
y = 42
|
||||
|
||||
\{} -> x + y
|
||||
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
platform roc/quicksort
|
||||
provides []
|
||||
requires {}
|
||||
platform examples/closure
|
||||
requires { main : Effect {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
effects Effect {}
|
||||
provides [ mainForHost ]
|
||||
effects Effect
|
||||
{
|
||||
putChar : Int -> Effect {},
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app Main provides [ main ] imports [ Effect, RBTree ]
|
||||
app "effect-example" provides [ main ] imports [ Effect, RBTree ]
|
||||
|
||||
toAndFro : Int
|
||||
toAndFro =
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
platform folkertdev/foo
|
||||
provides [ mainForHost ]
|
||||
requires { main : Effect {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects Effect
|
||||
{
|
||||
putChar : Int -> Effect {},
|
||||
|
|
1
examples/hello-world/.gitignore
vendored
Normal file
1
examples/hello-world/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
hello-world
|
|
@ -1,4 +1,4 @@
|
|||
app Hello provides [ main ] imports []
|
||||
app "hello-world" provides [ main ] to "./platform"
|
||||
|
||||
greeting =
|
||||
hi = "Hello"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
platform roc/quicksort
|
||||
provides []
|
||||
requires {}
|
||||
platform examples/hello-world
|
||||
requires { main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
effects Effect {}
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
|||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "Hello_main_1_exposed"]
|
||||
#[link_name = "roc__main_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
|
1
examples/multi-module/.gitignore
vendored
Normal file
1
examples/multi-module/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
quicksort
|
|
@ -1,4 +1,4 @@
|
|||
app Quicksort provides [ quicksort ] imports [ Utils.{swap} ]
|
||||
app "quicksort" imports [ Utils.{ swap } ] provides [ quicksort ] to "./platform"
|
||||
|
||||
|
||||
quicksort : List Int -> List Int
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
platform roc/quicksort
|
||||
provides []
|
||||
requires {}
|
||||
platform examples/multi-module
|
||||
requires { quicksort : List (Num a) -> List (Num a) }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
effects Effect {}
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocList;
|
|||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "Quicksort_quicksort_1_exposed"]
|
||||
#[link_name = "roc__quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
|
1
examples/quicksort/.gitignore
vendored
Normal file
1
examples/quicksort/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
quicksort
|
|
@ -1,4 +1,4 @@
|
|||
app Quicksort provides [ quicksort ] imports []
|
||||
app "quicksort" provides [ quicksort ] to "./platform"
|
||||
|
||||
quicksort = \originalList ->
|
||||
|
||||
|
@ -54,9 +54,3 @@ quicksort = \originalList ->
|
|||
|
||||
n = List.len originalList
|
||||
quicksortHelp originalList 0 (n - 1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
platform roc/quicksort
|
||||
provides []
|
||||
requires {}
|
||||
platform examples/quicksort
|
||||
requires { quicksort : List (Num a) -> List (Num a) }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ main ]
|
||||
effects Effect {}
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocList;
|
|||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "Quicksort_quicksort_1_exposed"]
|
||||
#[link_name = "roc__quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app Quicksort provides [ quicksort ] imports []
|
||||
app "quicksort" packages { base: "./platform" } provides [ quicksort ] to base
|
||||
|
||||
quicksort : List Int -> List Int
|
||||
quicksort = \originalList -> helper originalList
|
||||
|
@ -66,4 +66,3 @@ helper = \originalList ->
|
|||
# Absolutely make the `originalList` Shared by using it again here
|
||||
# but this branch is not evaluated, so should not affect performance
|
||||
List.set originalList 0 (List.len originalList)
|
||||
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
platform roc/quicksort
|
||||
provides []
|
||||
requires {}
|
||||
platform examples/shared-quicksort
|
||||
requires { main : Effect {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
effects Effect {}
|
||||
provides [ mainForHost ]
|
||||
effects Effect
|
||||
{
|
||||
putChar : Int -> Effect {},
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use roc_std::RocList;
|
|||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "Main_quicksort_1_exposed"]
|
||||
#[link_name = "_quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue