Merge branch 'trunk' into rvcas/list_functions

This commit is contained in:
Lucas 2020-11-24 09:07:28 -05:00 committed by GitHub
commit dfcf7bb7a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 1971 additions and 1318 deletions

View file

@ -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:

View file

@ -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

View file

@ -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.

View file

@ -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!

View file

@ -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);
if !memory_errors.is_empty() {
panic!("{:?}", memory_errors);
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,

View file

@ -1,4 +1,3 @@
app
host.o
c_host.o
app.dSYM
*.o
*.dSYM

View file

@ -0,0 +1 @@
multi-dep-str

View file

@ -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

View file

@ -1,5 +1,7 @@
platform roc/quicksort
provides []
requires {}
platform examples/multi-module
requires { main : Str }
exposes []
packages {}
imports []
provides [ main ]
effects Effect {}

View file

@ -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>) -> ();
}

View file

@ -0,0 +1 @@
multi-dep-thunk

View file

@ -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 {}

View file

@ -1,5 +1,7 @@
platform roc/quicksort
provides []
requires {}
platform examples/multi-dep-thunk
requires { main : Str }
exposes []
packages {}
imports []
provides [ main ]
effects Effect {}

View file

@ -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>) -> ();
}

View file

@ -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)]

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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 ]

View file

@ -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

View file

@ -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 ]

View file

@ -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!

View file

@ -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,21 +2111,35 @@ 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.
// If it isn't, report a problem!
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();
@ -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),
}
}

View file

@ -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 {}

View file

@ -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 ->

View file

@ -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)
@ -53,5 +53,5 @@ quicksort = \originalList ->
n = List.len originalList
n = List.len originalList
quicksortHelp originalList 0 (n - 1)

View file

@ -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");
assert_eq!(expected_name, &InlinableString::from(module_name));
// 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
}
@ -238,31 +242,34 @@ mod test_load {
"RBTree",
indoc!(
r#"
interface RBTree exposes [ Dict, empty ] imports []
interface RBTree exposes [ Dict, empty ] imports []
# The color of a node. Leaves are considered Black.
NodeColor : [ Red, Black ]
# The color of a node. Leaves are considered Black.
NodeColor : [ Red, Black ]
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
# Create an empty dictionary.
empty : Dict k v
empty =
Empty
"#
# Create an empty dictionary.
empty : Dict k v
empty =
Empty
"#
),
),
(
"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
empty : RBTree.Dict Int Int
empty = RBTree.empty
main = empty
"#
main = empty
"#
),
),
];

View file

@ -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");
assert_eq!(expected_name, &InlinableString::from(module_name));
// 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
}

View file

@ -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

View file

@ -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)

View file

@ -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)
}
}
},

View file

@ -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")
}

View file

@ -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())
),
|(
(after_app_keyword, name),
(
((before_provides, after_provides), provides),
((before_imports, after_imports), imports),
skip_first!(
ascii_string("app"),
and!(space1(1), loc!(string_literal::parse()))
),
)| {
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
(
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_requires, after_requires), requires),
(((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));

View file

@ -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,

View file

@ -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)

View file

@ -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);

View file

@ -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
View file

@ -1,5 +1,3 @@
app
host.o
c_host.o
roc_app.o
app.dSYM
*.o
*.dSYM

View file

@ -1,9 +1,8 @@
app Closure provides [ makeClosure ] imports []
app "closure" provides [ makeClosure ] to "./platform/"
makeClosure : ({} -> Int) as MyClosure
makeClosure =
makeClosure =
x = 42
y = 42
\{} -> x + y

View file

@ -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
}

View file

@ -1,4 +1,4 @@
app Main provides [ main ] imports [ Effect, RBTree ]
app "effect-example" provides [ main ] imports [ Effect, RBTree ]
toAndFro : Int
toAndFro =

View file

@ -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
View file

@ -0,0 +1 @@
hello-world

View file

@ -1,4 +1,4 @@
app Hello provides [ main ] imports []
app "hello-world" provides [ main ] to "./platform"
greeting =
hi = "Hello"

View file

@ -1,5 +1,7 @@
platform roc/quicksort
provides []
requires {}
platform examples/hello-world
requires { main : Str }
exposes []
packages {}
imports []
provides [ main ]
effects Effect {}

View file

@ -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
View file

@ -0,0 +1 @@
quicksort

View file

@ -1,8 +1,8 @@
app Quicksort provides [ quicksort ] imports [ Utils.{swap} ]
app "quicksort" imports [ Utils.{ swap } ] provides [ quicksort ] to "./platform"
quicksort : List Int -> List Int
quicksort = \originalList ->
quicksort = \originalList ->
quicksortHelp : List (Num a), Int, Int -> List (Num a)
quicksortHelp = \list, low, high ->
if low < high then
@ -43,5 +43,5 @@ quicksort = \originalList ->
n = List.len originalList
n = List.len originalList
quicksortHelp originalList 0 (n - 1)

View file

@ -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 {}

View file

@ -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
View file

@ -0,0 +1 @@
quicksort

View file

@ -1,4 +1,4 @@
app Quicksort provides [ quicksort ] imports []
app "quicksort" provides [ quicksort ] to "./platform"
quicksort = \originalList ->
@ -52,11 +52,5 @@ quicksort = \originalList ->
_ ->
[]
n = List.len originalList
n = List.len originalList
quicksortHelp originalList 0 (n - 1)

View file

@ -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 {}

View file

@ -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>>) -> ();
}

View file

@ -1,11 +1,11 @@
app Quicksort provides [ quicksort ] imports []
app "quicksort" packages { base: "./platform" } provides [ quicksort ] to base
quicksort : List Int -> List Int
quicksort = \originalList -> helper originalList
helper : List Int -> List Int
helper = \originalList ->
quicksortHelp : List (Num a), Int, Int -> List (Num a)
quicksortHelp = \list, low, high ->
if low < high then
@ -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)

View file

@ -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
}

View file

@ -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>>) -> ();
}