Merge branch 'main' into builtin-task

This commit is contained in:
Sam Mohr 2024-06-26 03:17:29 -07:00
commit cd488300fd
No known key found for this signature in database
GPG key ID: EA41D161A3C1BC99
24 changed files with 964 additions and 953 deletions

View file

@ -1436,6 +1436,54 @@ mod cli_run {
);
}
#[test]
#[serial(multi_dep_thunk)]
#[cfg_attr(windows, ignore)]
fn run_transitive_deps_app() {
check_output_with_stdin(
&fixture_file("transitive-deps", "direct-one.roc"),
&[],
&[],
&[],
&[],
"[One imports Two: From two]\n",
UseValgrind::Yes,
TestCliCommands::Run,
);
}
#[test]
#[serial(multi_dep_thunk)]
#[cfg_attr(windows, ignore)]
fn run_transitive_and_direct_dep_app() {
check_output_with_stdin(
&fixture_file("transitive-deps", "direct-one-and-two.roc"),
&[],
&[],
&[],
&[],
"[One imports Two: From two] | From two\n",
UseValgrind::Yes,
TestCliCommands::Run,
);
}
#[test]
#[serial(multi_dep_thunk)]
#[cfg_attr(windows, ignore)]
fn run_double_transitive_dep_app() {
check_output_with_stdin(
&fixture_file("transitive-deps", "direct-zero.roc"),
&[],
&[],
&[],
&[],
"[Zero imports One: [One imports Two: From two]]\n",
UseValgrind::Yes,
TestCliCommands::Run,
);
}
#[test]
fn known_type_error() {
check_compile_error(

View file

@ -0,0 +1,10 @@
app [main] {
pf: platform "../packages/platform/main.roc",
one: "one/main.roc",
two: "two/main.roc",
}
import one.One
import two.Two
main = "$(One.example) | $(Two.example)"

View file

@ -0,0 +1,8 @@
app [main] {
pf: platform "../packages/platform/main.roc",
one: "one/main.roc",
}
import one.One
main = One.example

View file

@ -0,0 +1,8 @@
app [main] {
pf: platform "../packages/platform/main.roc",
zero: "zero/main.roc",
}
import zero.Zero
main = Zero.example

View file

@ -0,0 +1,5 @@
module [example]
import two.Two
example = "[One imports Two: $(Two.example)]"

View file

@ -0,0 +1,3 @@
package [One] {
two: "../two/main.roc",
}

View file

@ -0,0 +1,3 @@
module [example]
example = "From two"

View file

@ -0,0 +1 @@
package [Two] {}

View file

@ -0,0 +1,5 @@
module [example]
import one.One
example = "[Zero imports One: $(One.example)]"

View file

@ -0,0 +1,3 @@
package [Zero] {
one: "../one/main.roc"
}

View file

@ -610,7 +610,7 @@ mod suffixed_tests {
else
line "fail"
"#,
r##"Defs { tags: [Index(2147483648)], regions: [@0-286], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main" }, @23-286 Defs(Defs { tags: [Index(2147483650), Index(2147483651)], regions: [@32-49, @76-94], space_before: [Slice(start = 0, length = 0), Slice(start = 0, length = 1)], space_after: [Slice(start = 0, length = 0), Slice(start = 1, length = 0)], spaces: [Newline], type_defs: [], value_defs: [Body(@23-29 Identifier { ident: "isTrue" }, @32-49 Apply(@32-39 Var { module_name: "Task", ident: "ok" }, [@40-49 Var { module_name: "Bool", ident: "true" }], Space)), Body(@66-73 Identifier { ident: "isFalse" }, @76-94 Apply(@76-83 Var { module_name: "Task", ident: "ok" }, [@84-94 Var { module_name: "Bool", ident: "false" }], Space)), Body(@23-29 Identifier { ident: "isTrue" }, @32-49 Apply(@32-39 Var { module_name: "Task", ident: "ok" }, [@40-49 Var { module_name: "Bool", ident: "true" }], Space)), Body(@66-73 Identifier { ident: "isFalse" }, @76-94 Apply(@76-83 Var { module_name: "Task", ident: "ok" }, [@84-94 Var { module_name: "Bool", ident: "false" }], Space))] }, @115-123 Apply(@115-123 Var { module_name: "Task", ident: "await" }, [@115-123 Var { module_name: "", ident: "isFalse" }, @115-123 Closure([@115-123 Identifier { ident: "#!a0" }], @112-286 If([(@115-123 Var { module_name: "", ident: "#!a0" }, @149-160 Apply(@149-153 Var { module_name: "", ident: "line" }, [@154-160 Str(PlainLine("fail"))], Space))], @185-192 Apply(@185-192 Var { module_name: "Task", ident: "await" }, [@185-192 Var { module_name: "", ident: "isTrue" }, @185-192 Closure([@185-192 Identifier { ident: "#!a1" }], @112-286 If([(@185-192 Var { module_name: "", ident: "#!a1" }, @219-233 Apply(@219-223 Var { module_name: "", ident: "line" }, [@224-233 Str(PlainLine("success"))], Space))], @275-286 Apply(@275-279 Var { module_name: "", ident: "line" }, [@280-286 Str(PlainLine("fail"))], Space)))], BangSuffix)))], BangSuffix)))] }"##,
r##"Defs { tags: [Index(2147483648)], regions: [@0-286], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main" }, @0-286 Defs(Defs { tags: [Index(2147483650), Index(2147483651)], regions: [@32-49, @76-94], space_before: [Slice(start = 0, length = 0), Slice(start = 0, length = 1)], space_after: [Slice(start = 0, length = 0), Slice(start = 1, length = 0)], spaces: [Newline], type_defs: [], value_defs: [Body(@23-29 Identifier { ident: "isTrue" }, @32-49 Apply(@32-39 Var { module_name: "Task", ident: "ok" }, [@40-49 Var { module_name: "Bool", ident: "true" }], Space)), Body(@66-73 Identifier { ident: "isFalse" }, @76-94 Apply(@76-83 Var { module_name: "Task", ident: "ok" }, [@84-94 Var { module_name: "Bool", ident: "false" }], Space)), Body(@23-29 Identifier { ident: "isTrue" }, @32-49 Apply(@32-39 Var { module_name: "Task", ident: "ok" }, [@40-49 Var { module_name: "Bool", ident: "true" }], Space)), Body(@66-73 Identifier { ident: "isFalse" }, @76-94 Apply(@76-83 Var { module_name: "Task", ident: "ok" }, [@84-94 Var { module_name: "Bool", ident: "false" }], Space))] }, @115-123 Apply(@115-123 Var { module_name: "Task", ident: "await" }, [@115-123 Var { module_name: "", ident: "isFalse" }, @115-123 Closure([@115-123 Identifier { ident: "#!a0" }], @112-286 If([(@115-123 Var { module_name: "", ident: "#!a0" }, @149-160 Apply(@149-153 Var { module_name: "", ident: "line" }, [@154-160 Str(PlainLine("fail"))], Space))], @185-192 Apply(@185-192 Var { module_name: "Task", ident: "await" }, [@185-192 Var { module_name: "", ident: "isTrue" }, @185-192 Closure([@185-192 Identifier { ident: "#!a1" }], @112-286 If([(@185-192 Var { module_name: "", ident: "#!a1" }, @219-233 Apply(@219-223 Var { module_name: "", ident: "line" }, [@224-233 Str(PlainLine("success"))], Space))], @275-286 Apply(@275-279 Var { module_name: "", ident: "line" }, [@280-286 Str(PlainLine("fail"))], Space)))], BangSuffix)))], BangSuffix)))] }"##,
);
}
@ -681,7 +681,7 @@ mod suffixed_tests {
CMD.new "cp"
|> mapErr! ERR
"#,
r#"Defs { tags: [Index(2147483648)], regions: [@0-103], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "copy" }, @7-103 Closure([@8-9 Identifier { ident: "a" }, @10-11 Identifier { ident: "b" }], @36-42 Apply(@36-42 Var { module_name: "Task", ident: "await" }, [@36-42 Apply(@36-42 Var { module_name: "", ident: "line" }, [@37-42 Str(PlainLine("FOO"))], Space), @36-42 Closure([@36-42 RecordDestructure([])], @60-103 Apply(@60-103 Var { module_name: "", ident: "mapErr" }, [@60-72 Apply(@60-67 Var { module_name: "CMD", ident: "new" }, [@68-72 Str(PlainLine("cp"))], Space), @100-103 Tag("ERR")], BinOp(Pizza)))], BangSuffix)))] }"#,
r#"Defs { tags: [Index(2147483648)], regions: [@0-103], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "copy" }, @0-103 Closure([@8-9 Identifier { ident: "a" }, @10-11 Identifier { ident: "b" }], @36-42 Apply(@36-42 Var { module_name: "Task", ident: "await" }, [@36-42 Apply(@36-42 Var { module_name: "", ident: "line" }, [@37-42 Str(PlainLine("FOO"))], Space), @36-42 Closure([@36-42 RecordDestructure([])], @60-103 Apply(@60-103 Var { module_name: "", ident: "mapErr" }, [@60-72 Apply(@60-67 Var { module_name: "CMD", ident: "new" }, [@68-72 Str(PlainLine("cp"))], Space), @100-103 Tag("ERR")], BinOp(Pizza)))], BangSuffix)))] }"#,
);
}
@ -880,6 +880,26 @@ mod suffixed_tests {
r#"Defs { tags: [Index(2147483648)], regions: [@0-159], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main" }, @0-159 When(@28-29 Var { module_name: "", ident: "a" }, [WhenBranch { patterns: [@53-54 NumLiteral("0")], value: @82-159 When(@87-88 Var { module_name: "", ident: "b" }, [WhenBranch { patterns: [@120-121 NumLiteral("1")], value: @157-159 Var { module_name: "", ident: "c" }, guard: None }]), guard: None }]))] }"#,
);
}
#[test]
fn deps_final_expr() {
run_test(
r#"
main =
when x is
A ->
y = 42
if a then
b!
else
c!
B ->
d!
"#,
r#"Defs { tags: [Index(2147483648)], regions: [@0-266], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@0-4 Identifier { ident: "main" }, @0-266 When(@28-29 Var { module_name: "", ident: "x" }, [WhenBranch { patterns: [@53-54 Tag("A")], value: @82-214 Defs(Defs { tags: [Index(2147483649)], regions: [@86-88], space_before: [Slice(start = 0, length = 0)], space_after: [Slice(start = 0, length = 0)], spaces: [], type_defs: [], value_defs: [Body(@82-83 Identifier { ident: "y" }, @86-88 Num("42")), Body(@82-83 Identifier { ident: "y" }, @86-88 Num("42"))] }, @114-214 If([(@117-118 Var { module_name: "", ident: "a" }, @152-154 Var { module_name: "", ident: "b" })], @212-214 Var { module_name: "", ident: "c" })), guard: None }, WhenBranch { patterns: [@235-236 Tag("B")], value: @264-266 Var { module_name: "", ident: "d" }, guard: None }]))] }"#,
);
}
}
#[cfg(test)]

View file

@ -2,4 +2,15 @@
A tool to debug the solver (checker + inference + specialization engine).
See [the document](https://rwx.notion.site/Type-debugging-tools-de42260060784cacbaf08ea4d61e0eb9?pvs=4).
## Usage
If you run into a typechecking bug and want to try to diagnose it, after minimizing, you can run a debug version of the compiler with `ROC_CHECKMATE=1` set.
That will spit out a JSON file of the form `checkmate_<timestamp>.json`.
You can then load them into the UI and investigate the unifications. The UI can
be started with `npm start` in the `www` directory.
Note that you will need Node.js and NPM installed to run the UI. Unlike other
subprojects, this project requires external dependencies because it is fully
optional, as an internal developer tool. A motivated contributor is free to
rewrite the UI in a different framework, including Roc itself.

View file

@ -1560,15 +1560,7 @@ pub fn load_single_threaded<'a>(
// now we just manually interleave stepping the state "thread" and the worker "thread"
loop {
match state_thread_step(
arena,
state,
&src_dir,
worker_listeners,
&injector,
&msg_tx,
&msg_rx,
) {
match state_thread_step(arena, state, worker_listeners, &injector, &msg_tx, &msg_rx) {
Ok(ControlFlow::Break(done)) => return Ok(done),
Ok(ControlFlow::Continue(new_state)) => {
state = new_state;
@ -1595,7 +1587,6 @@ pub fn load_single_threaded<'a>(
fn state_thread_step<'a>(
arena: &'a Bump,
state: State<'a>,
src_dir: &Path,
worker_listeners: &'a [Sender<WorkerMsg>],
injector: &Injector<BuildTask<'a>>,
msg_tx: &crossbeam::channel::Sender<Msg<'a>>,
@ -1704,7 +1695,6 @@ fn state_thread_step<'a>(
let res_state = update(
state,
src_dir,
msg,
msg_tx.clone(),
injector,
@ -2050,15 +2040,8 @@ fn load_multi_threaded<'a>(
// The root module will have already queued up messages to process,
// and processing those messages will in turn queue up more messages.
loop {
match state_thread_step(
arena,
state,
&src_dir,
worker_listeners,
&injector,
&msg_tx,
&msg_rx,
) {
match state_thread_step(arena, state, worker_listeners, &injector, &msg_tx, &msg_rx)
{
Ok(ControlFlow::Break(load_result)) => {
shut_down_worker_threads!();
@ -2174,7 +2157,6 @@ fn extend_module_with_builtin_import(module: &mut ParsedModule, module_id: Modul
fn update<'a>(
mut state: State<'a>,
src_dir: &Path,
msg: Msg<'a>,
msg_tx: MsgSender<'a>,
injector: &Injector<BuildTask<'a>>,
@ -2203,11 +2185,14 @@ fn update<'a>(
if !header.packages.is_empty() {
let mut shorthands = state.arc_shorthands.lock();
let mut parent_dir = header.module_path.clone();
parent_dir.pop();
register_package_shorthands(
&mut shorthands,
&header.packages,
&header.module_path,
src_dir,
&parent_dir,
&state.cache_dir,
)?;
}
@ -3348,6 +3333,7 @@ fn load_package_from_disk<'a>(
arena: &'a Bump,
filename: &Path,
shorthand: &'a str,
roc_cache_dir: RocCacheDir,
app_module_id: Option<ModuleId>,
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: SharedIdentIdsByModule,
@ -3406,20 +3392,41 @@ fn load_package_from_disk<'a>(
},
parser_state,
)) => {
let (_, _, package_module_msg) = build_package_header(
let mut parent_dir = filename.to_path_buf();
parent_dir.pop();
let packages = unspace(arena, header.packages.value.items);
let (_, _, header) = build_package_header(
arena,
Some(shorthand),
false, // cannot be the root if loaded as a package
filename.to_path_buf(),
parser_state,
module_ids,
ident_ids_by_module,
module_ids.clone(),
ident_ids_by_module.clone(),
&header,
comments,
pkg_module_timing,
)?;
Ok(Msg::Header(package_module_msg))
let filename = header.module_path.clone();
let mut messages = Vec::with_capacity(packages.len() + 1);
messages.push(Msg::Header(header));
load_packages(
packages,
&mut messages,
roc_cache_dir,
parent_dir,
arena,
None,
module_ids,
ident_ids_by_module,
filename,
);
Ok(Msg::Many(messages))
}
Ok((
ast::Module {
@ -3428,6 +3435,11 @@ fn load_package_from_disk<'a>(
},
parser_state,
)) => {
let mut parent_dir = filename.to_path_buf();
parent_dir.pop();
let packages = unspace(arena, header.packages.item.items);
let exposes_ids = get_exposes_ids(
header.exposes.item.items,
arena,
@ -3436,21 +3448,37 @@ fn load_package_from_disk<'a>(
);
// make a `platform` module that ultimately exposes `main` to the host
let (_, _, platform_module_msg) = build_platform_header(
let (_, _, header) = build_platform_header(
arena,
Some(shorthand),
false, // cannot be the root if loaded as a package
app_module_id,
filename.to_path_buf(),
parser_state,
module_ids,
module_ids.clone(),
exposes_ids.into_bump_slice(),
&header,
comments,
pkg_module_timing,
)?;
Ok(Msg::Header(platform_module_msg))
let filename = header.module_path.clone();
let mut messages = Vec::with_capacity(packages.len() + 1);
messages.push(Msg::Header(header));
load_packages(
packages,
&mut messages,
roc_cache_dir,
parent_dir,
arena,
None,
module_ids,
ident_ids_by_module,
filename,
);
Ok(Msg::Many(messages))
}
Err(fail) => Err(LoadingProblem::ParsingFailed(
fail.map_problem(SyntaxError::Header)
@ -4090,6 +4118,7 @@ fn load_packages<'a>(
arena,
&root_module_path,
shorthand,
roc_cache_dir,
app_module_id,
module_ids.clone(),
ident_ids_by_module.clone(),

View file

@ -575,8 +575,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
Expr::Closure(_, sub_loc_expr) => is_expr_suffixed(&sub_loc_expr.value),
// expressions inside a Defs
// note we ignore the final expression as it should not be suffixed
Expr::Defs(defs, _) => {
Expr::Defs(defs, expr) => {
let any_defs_suffixed = defs.tags.iter().any(|tag| match tag.split() {
Ok(_) => false,
Err(value_index) => match defs.value_defs[value_index.index()] {
@ -586,7 +585,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
},
});
any_defs_suffixed
any_defs_suffixed || is_expr_suffixed(&expr.value)
}
Expr::Float(_) => false,
Expr::Num(_) => false,

View file

@ -1,5 +1,6 @@
use crate::ast::CommentOrNewline;
use crate::ast::Spaceable;
use crate::parser::succeed;
use crate::parser::Progress;
use crate::parser::SpaceProblem;
use crate::parser::{self, and, backtrackable, BadInputError, Parser, Progress::*};
@ -70,7 +71,7 @@ where
parser,
one_of![
backtrackable(space0_e(indent_after_problem)),
succeed!(&[] as &[_]),
succeed(&[] as &[_]),
],
),
),
@ -89,7 +90,7 @@ where
spaces(),
and(
parser,
one_of![backtrackable(spaces()), succeed!(&[] as &[_]),],
one_of![backtrackable(spaces()), succeed(&[] as &[_]),],
),
),
spaces_around_help,
@ -138,7 +139,7 @@ where
E: 'a + SpaceProblem,
{
parser::map_with_arena(
and!(spaces(), parser),
and(spaces(), parser),
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Loc<S>)| {
if space_list.is_empty() {
loc_expr
@ -161,7 +162,7 @@ where
E: 'a + SpaceProblem,
{
parser::map_with_arena(
and!(space0_e(indent_problem), parser),
and(space0_e(indent_problem), parser),
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Loc<S>)| {
if space_list.is_empty() {
loc_expr
@ -184,7 +185,7 @@ where
E: 'a + SpaceProblem,
{
parser::map_with_arena(
and!(parser, space0_e(indent_problem)),
and(parser, space0_e(indent_problem)),
|arena: &'a Bump, (loc_expr, space_list): (Loc<S>, &'a [CommentOrNewline<'a>])| {
if space_list.is_empty() {
loc_expr

View file

@ -14,9 +14,11 @@ use crate::ident::{
};
use crate::module::module_name_help;
use crate::parser::{
self, backtrackable, byte, byte_indent, increment_min_indent, line_min_indent, optional,
reset_min_indent, sep_by1, sep_by1_e, set_min_indent, specialize_err, specialize_err_ref, then,
two_bytes, EClosure, EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber,
self, and, backtrackable, between, byte, byte_indent, collection_inner,
collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first,
line_min_indent, loc, map, map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e,
set_min_indent, skip_first, skip_second, specialize_err, specialize_err_ref, then, two_bytes,
zero_or_more, EClosure, EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber,
EPattern, ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
};
use crate::pattern::{closure_param, loc_implements_parser};
@ -48,9 +50,9 @@ pub fn test_parse_expr<'a>(
arena: &'a bumpalo::Bump,
state: State<'a>,
) -> Result<Loc<Expr<'a>>, EExpr<'a>> {
let parser = skip_second!(
let parser = skip_second(
space0_before_optional_after(loc_expr(true), EExpr::IndentStart, EExpr::IndentEnd),
expr_end()
expr_end(),
);
match parser.parse(arena, state, min_indent) {
@ -84,12 +86,12 @@ pub fn expr_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EInParens<'a>> {
then(
loc!(collection_trailing_sep_e!(
loc(collection_trailing_sep_e(
byte(b'(', EInParens::Open),
specialize_err_ref(EInParens::Expr, loc_expr(false)),
byte(b',', EInParens::End),
byte(b')', EInParens::End),
Expr::SpaceBefore
Expr::SpaceBefore,
)),
move |arena, state, _, loc_elements| {
let elements = loc_elements.value;
@ -121,10 +123,10 @@ fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EInParens<'a>
}
fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
map_with_arena!(
loc!(and!(
map_with_arena(
loc(and(
specialize_err(EExpr::InParens, loc_expr_in_parens_help()),
record_field_access_chain()
record_field_access_chain(),
)),
move |arena: &'a Bump, value: Loc<(Loc<Expr<'a>>, Vec<'a, Suffix<'a>>)>| {
let Loc {
@ -143,27 +145,27 @@ fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>
}
Loc::at(region, value)
}
},
)
}
fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr<'a>> {
zero_or_more!(one_of!(
skip_first!(
zero_or_more(one_of!(
skip_first(
byte(b'.', EExpr::Access),
specialize_err(
|_, pos| EExpr::Access(pos),
one_of!(
map!(lowercase_ident(), |x| Suffix::Accessor(
map(lowercase_ident(), |x| Suffix::Accessor(
Accessor::RecordField(x)
)),
map!(integer_ident(), |x| Suffix::Accessor(Accessor::TupleIndex(
map(integer_ident(), |x| Suffix::Accessor(Accessor::TupleIndex(
x
))),
)
)
),
map!(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang),
map(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang),
))
}
@ -174,18 +176,18 @@ fn loc_term_or_underscore_or_conditional<'a>(
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!(
loc_expr_in_parens_etc_help(),
loc!(specialize_err(EExpr::If, if_expr_help(options))),
loc!(specialize_err(EExpr::When, when::expr_help(options))),
loc!(specialize_err(EExpr::Str, string_like_literal_help())),
loc!(specialize_err(
loc(specialize_err(EExpr::If, if_expr_help(options))),
loc(specialize_err(EExpr::When, when::expr_help(options))),
loc(specialize_err(EExpr::Str, string_like_literal_help())),
loc(specialize_err(
EExpr::Number,
positive_number_literal_help()
)),
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(crash_kw()),
loc!(underscore_expression()),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(crash_kw()),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
ident_seq(),
)
}
@ -197,15 +199,15 @@ fn loc_term_or_underscore<'a>(
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!(
loc_expr_in_parens_etc_help(),
loc!(specialize_err(EExpr::Str, string_like_literal_help())),
loc!(specialize_err(
loc(specialize_err(EExpr::Str, string_like_literal_help())),
loc(specialize_err(
EExpr::Number,
positive_number_literal_help()
)),
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(underscore_expression()),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
ident_seq(),
)
}
@ -213,14 +215,14 @@ fn loc_term_or_underscore<'a>(
fn loc_term<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!(
loc_expr_in_parens_etc_help(),
loc!(specialize_err(EExpr::Str, string_like_literal_help())),
loc!(specialize_err(
loc(specialize_err(EExpr::Str, string_like_literal_help())),
loc(specialize_err(
EExpr::Number,
positive_number_literal_help()
)),
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
ident_seq(),
)
}
@ -235,7 +237,7 @@ fn parse_ident_seq<'a>(
min_indent: u32,
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
let (_, loc_ident, state) =
loc!(assign_or_destructure_identifier()).parse(arena, state, min_indent)?;
loc(assign_or_destructure_identifier()).parse(arena, state, min_indent)?;
let expr = ident_to_expr(arena, loc_ident.value);
let (_p, suffixes, state) = record_field_access_chain()
.trace("record_field_access_chain")
@ -280,7 +282,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
let initial = state.clone();
let (_, (loc_op, loc_expr), state) =
and!(loc!(unary_negate()), loc_term(options)).parse(arena, state, min_indent)?;
and(loc(unary_negate()), loc_term(options)).parse(arena, state, min_indent)?;
let loc_expr = numeric_negate_expression(arena, initial, loc_op, loc_expr, &[]);
@ -290,10 +292,10 @@ fn loc_possibly_negative_or_negated_term<'a>(
one_of![
parse_unary_negate,
// this will parse negative numbers, which the unary negate thing up top doesn't (for now)
loc!(specialize_err(EExpr::Number, number_literal_help())),
loc!(map_with_arena!(
and!(
loc!(byte(b'!', EExpr::Start)),
loc(specialize_err(EExpr::Number, number_literal_help())),
loc(map_with_arena(
and(
loc(byte(b'!', EExpr::Start)),
space0_before_e(loc_term(options), EExpr::IndentStart)
),
|arena: &'a Bump, (loc_op, loc_expr): (Loc<_>, _)| {
@ -333,13 +335,13 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of![
loc!(specialize_err(EExpr::If, if_expr_help(options))),
loc!(specialize_err(EExpr::When, when::expr_help(options))),
loc!(specialize_err(EExpr::Expect, expect_help(options))),
loc!(specialize_err(EExpr::Dbg, dbg_help(options))),
loc!(import_help(options)),
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(expr_operator_chain(options)),
loc(specialize_err(EExpr::If, if_expr_help(options))),
loc(specialize_err(EExpr::When, when::expr_help(options))),
loc(specialize_err(EExpr::Expect, expect_help(options))),
loc(specialize_err(EExpr::Dbg, dbg_help(options))),
loc(import_help(options)),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(expr_operator_chain(options)),
fail_expr_start_e()
]
.trace("expr_start")
@ -633,7 +635,7 @@ pub fn parse_single_def<'a>(
let parse_expect_vanilla = crate::parser::keyword(crate::keyword::EXPECT, EExpect::Expect);
let parse_expect_fx = crate::parser::keyword(crate::keyword::EXPECT_FX, EExpect::Expect);
let parse_expect = either!(parse_expect_fx, parse_expect_vanilla);
let parse_expect = either(parse_expect_fx, parse_expect_vanilla);
match space0_after_e(crate::pattern::loc_pattern_help(), EPattern::IndentEnd).parse(
arena,
@ -956,12 +958,12 @@ pub fn parse_single_def<'a>(
fn import<'a>() -> impl Parser<'a, (Loc<ValueDef<'a>>, &'a [CommentOrNewline<'a>]), EImport<'a>> {
then(
and!(
loc!(skip_first!(
and(
loc(skip_first(
parser::keyword(keyword::IMPORT, EImport::Import),
increment_min_indent(one_of!(import_body(), import_ingested_file_body()))
increment_min_indent(one_of!(import_body(), import_ingested_file_body())),
)),
space0_e(EImport::EndNewline)
space0_e(EImport::EndNewline),
),
|_arena, state, progress, (import, spaces_after)| {
if !spaces_after.is_empty() || state.has_reached_end() {
@ -975,23 +977,23 @@ fn import<'a>() -> impl Parser<'a, (Loc<ValueDef<'a>>, &'a [CommentOrNewline<'a>
}
fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
map!(
map(
record!(ModuleImport {
before_name: space0_e(EImport::IndentStart),
name: loc!(imported_module_name()),
name: loc(imported_module_name()),
params: optional(specialize_err(EImport::Params, import_params())),
alias: optional(import_as()),
exposed: optional(import_exposing())
}),
ValueDef::ModuleImport
ValueDef::ModuleImport,
)
}
fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<'a>> {
then(
and!(
and(
backtrackable(space0_e(EImportParams::Indent)),
specialize_err(EImportParams::Record, record_help())
specialize_err(EImportParams::Record, record_help()),
),
|arena, state, _, (before, record): (_, RecordHelp<'a>)| {
if let Some(update) = record.update {
@ -1021,7 +1023,7 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<
#[inline(always)]
fn imported_module_name<'a>() -> impl Parser<'a, ImportedModuleName<'a>, EImport<'a>> {
record!(ImportedModuleName {
package: optional(skip_second!(
package: optional(skip_second(
specialize_err(|_, pos| EImport::PackageShorthand(pos), lowercase_ident()),
byte(b'.', EImport::PackageShorthandDot)
)),
@ -1040,7 +1042,7 @@ fn import_as<'a>(
EImport::IndentAlias
),
item: then(
specialize_err(|_, pos| EImport::Alias(pos), loc!(unqualified_ident())),
specialize_err(|_, pos| EImport::Alias(pos), loc(unqualified_ident())),
|_arena, state, _progress, loc_ident| {
match loc_ident.value.chars().next() {
Some(first) if first.is_uppercase() => Ok((
@ -1073,9 +1075,9 @@ fn import_exposing<'a>() -> impl Parser<
EImport::IndentExposing,
EImport::ExposingListStart,
),
item: collection_trailing_sep_e!(
item: collection_trailing_sep_e(
byte(b'[', EImport::ExposingListStart),
loc!(import_exposed_name()),
loc(import_exposed_name()),
byte(b',', EImport::ExposingListEnd),
byte(b']', EImport::ExposingListEnd),
Spaced::SpaceBefore
@ -1086,25 +1088,25 @@ fn import_exposing<'a>() -> impl Parser<
#[inline(always)]
fn import_exposed_name<'a>(
) -> impl Parser<'a, crate::ast::Spaced<'a, crate::header::ExposedName<'a>>, EImport<'a>> {
map!(
map(
specialize_err(|_, pos| EImport::ExposedName(pos), unqualified_ident()),
|n| Spaced::Item(crate::header::ExposedName::new(n))
|n| Spaced::Item(crate::header::ExposedName::new(n)),
)
}
#[inline(always)]
fn import_ingested_file_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
map!(
map(
record!(IngestedFileImport {
before_path: space0_e(EImport::IndentStart),
path: loc!(specialize_err(
path: loc(specialize_err(
|_, pos| EImport::IngestedPath(pos),
string_literal::parse_str_literal()
)),
name: import_ingested_file_as(),
annotation: optional(import_ingested_file_annotation())
}),
ValueDef::IngestedFileImport
ValueDef::IngestedFileImport,
)
}
@ -1118,10 +1120,7 @@ fn import_ingested_file_as<'a>(
EImport::IndentAs,
EImport::IndentIngestedName
),
item: specialize_err(
|(), pos| EImport::IngestedName(pos),
loc!(lowercase_ident())
)
item: specialize_err(|(), pos| EImport::IngestedName(pos), loc(lowercase_ident()))
})
}
@ -1129,7 +1128,7 @@ fn import_ingested_file_as<'a>(
fn import_ingested_file_annotation<'a>() -> impl Parser<'a, IngestedFileAnnotation<'a>, EImport<'a>>
{
record!(IngestedFileAnnotation {
before_colon: skip_second!(
before_colon: skip_second(
backtrackable(space0_e(EImport::IndentColon)),
byte(b':', EImport::Colon)
),
@ -1535,7 +1534,7 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser<
),
EExpr<'a>,
> {
and!(
and(
specialize_err(
EExpr::Type,
space0_before_e(
@ -1545,8 +1544,8 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser<
),
optional(backtrackable(specialize_err(
EExpr::Type,
space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart,),
)))
space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart),
))),
)
}
@ -1717,30 +1716,28 @@ mod ability {
use super::*;
use crate::{
ast::{AbilityMember, Spaceable, Spaced},
parser::EAbility,
parser::{absolute_indented_seq, EAbility},
};
/// Parses a single ability demand line; see `parse_demand`.
fn parse_demand_help<'a>() -> impl Parser<'a, AbilityMember<'a>, EAbility<'a>> {
map!(
map(
// Require the type to be more indented than the name
absolute_indented_seq!(
specialize_err(|_, pos| EAbility::DemandName(pos), loc!(lowercase_ident())),
skip_first!(
and!(
absolute_indented_seq(
specialize_err(|_, pos| EAbility::DemandName(pos), loc(lowercase_ident())),
skip_first(
and(
// TODO: do we get anything from picking up spaces here?
space0_e(EAbility::DemandName),
byte(b':', EAbility::DemandColon)
byte(b':', EAbility::DemandColon),
),
specialize_err(EAbility::Type, type_annotation::located(true))
)
specialize_err(EAbility::Type, type_annotation::located(true)),
),
),
|(name, typ): (Loc<&'a str>, Loc<TypeAnnotation<'a>>)| {
AbilityMember {
name: name.map_owned(Spaced::Item),
typ,
}
}
|(name, typ): (Loc<&'a str>, Loc<TypeAnnotation<'a>>)| AbilityMember {
name: name.map_owned(Spaced::Item),
typ,
},
)
}
@ -2137,9 +2134,9 @@ fn parse_expr_end<'a>(
state: State<'a>,
initial_state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let parser = skip_first!(
let parser = skip_first(
crate::blankspace::check_indent(EExpr::IndentEnd),
loc_term_or_underscore(options)
loc_term_or_underscore(options),
);
match parser.parse(arena, state.clone(), min_indent) {
@ -2238,7 +2235,7 @@ fn parse_expr_end<'a>(
let before_op = state.clone();
// try an operator
let line_indent = state.line_indent();
match loc!(operator()).parse(arena, state.clone(), min_indent) {
match loc(operator()).parse(arena, state.clone(), min_indent) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((_, loc_op, state)) => {
expr_state.consume_spaces(arena);
@ -2578,14 +2575,14 @@ pub fn parse_top_level_defs<'a>(
fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
// closure_help_help(options)
map_with_arena!(
map_with_arena(
// After the first token, all other tokens must be indented past the start of the line
indented_seq_skip_first!(
indented_seq_skip_first(
// All closures start with a '\' - e.g. (\x -> x + 1)
byte_indent(b'\\', EClosure::Start),
// Once we see the '\', we're committed to parsing this as a closure.
// It may turn out to be malformed, but it is definitely a closure.
and!(
and(
// Parse the params
// Params are comma-separated
sep_by1_e(
@ -2597,22 +2594,22 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo
),
EClosure::Arg,
),
skip_first!(
skip_first(
// Parse the -> which separates params from body
two_bytes(b'-', b'>', EClosure::Arrow),
// Parse the body
space0_before_e(
specialize_err_ref(EClosure::Body, expr_start(options)),
EClosure::IndentBody
)
)
)
EClosure::IndentBody,
),
),
),
),
|arena: &'a Bump, (params, body)| {
let params: Vec<'a, Loc<Pattern<'a>>> = params;
let params: &'a [Loc<Pattern<'a>>] = params.into_bump_slice();
Expr::Closure(params, arena.alloc(body))
}
},
)
}
@ -2622,9 +2619,9 @@ mod when {
/// Parser for when expressions.
pub fn expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> {
map_with_arena!(
and!(
indented_seq_skip_first!(
map_with_arena(
and(
indented_seq_skip_first(
parser::keyword(keyword::WHEN, EWhen::When),
space0_around_e_no_after_indent_check(
specialize_err_ref(EWhen::Condition, expr_start(options)),
@ -2635,7 +2632,7 @@ mod when {
// ambiguity. The formatter will fix it up.
//
// We require that branches are indented relative to the line containing the `is`.
indented_seq_skip_first!(
indented_seq_skip_first(
parser::keyword(keyword::IS, EWhen::Is),
branches(options)
)
@ -2674,8 +2671,8 @@ mod when {
guard: loc_first_guard,
}));
let branch_parser = map!(
and!(
let branch_parser = map(
and(
then(
branch_alternatives(options, Some(pattern_indent_level)),
move |_arena, state, _, ((indent_column, loc_patterns), loc_guard)| {
@ -2687,7 +2684,7 @@ mod when {
}
},
),
branch_result(original_indent + 1)
branch_result(original_indent + 1),
),
|((patterns, guard), expr)| {
let patterns: Vec<'a, _> = patterns;
@ -2696,7 +2693,7 @@ mod when {
value: expr,
guard,
}
}
},
);
while !state.bytes().is_empty() {
@ -2728,11 +2725,11 @@ mod when {
check_for_arrow: false,
..options
};
and!(
and(
branch_alternatives_help(pattern_indent_level),
one_of![
map!(
skip_first!(
map(
skip_first(
parser::keyword(keyword::IF, EWhen::IfToken),
// TODO we should require space before the expression but not after
space0_around_ee(
@ -2747,7 +2744,7 @@ mod when {
Some
),
|_, s, _| Ok((NoProgress, None, s))
]
],
)
}
@ -2834,12 +2831,12 @@ mod when {
/// Parsing the righthandside of a branch in a when conditional.
fn branch_result<'a>(indent: u32) -> impl Parser<'a, Loc<Expr<'a>>, EWhen<'a>> {
move |arena, state, _min_indent| {
skip_first!(
skip_first(
two_bytes(b'-', b'>', EWhen::Arrow),
space0_before_e(
specialize_err_ref(EWhen::Branch, loc_expr(true)),
EWhen::IndentBranch,
)
),
)
.parse(arena, state, indent)
}
@ -2847,23 +2844,23 @@ mod when {
}
fn if_branch<'a>() -> impl Parser<'a, (Loc<Expr<'a>>, Loc<Expr<'a>>), EIf<'a>> {
skip_second!(
and!(
skip_second!(
skip_second(
and(
skip_second(
space0_around_ee(
specialize_err_ref(EIf::Condition, loc_expr(true)),
EIf::IndentCondition,
EIf::IndentThenToken,
),
parser::keyword(keyword::THEN, EIf::Then)
parser::keyword(keyword::THEN, EIf::Then),
),
space0_around_ee(
specialize_err_ref(EIf::ThenBranch, loc_expr(true)),
EIf::IndentThenBranch,
EIf::IndentElseToken,
)
),
),
parser::keyword(keyword::ELSE, EIf::Else)
parser::keyword(keyword::ELSE, EIf::Else),
)
}
@ -2956,9 +2953,9 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
// try to parse another `if`
// NOTE this drops spaces between the `else` and the `if`
let optional_if = and!(
let optional_if = and(
backtrackable(space0_e(EIf::IndentIf)),
parser::keyword(keyword::IF, EIf::If)
parser::keyword(keyword::IF, EIf::If),
);
match optional_if.parse(arena, state.clone(), min_indent) {
@ -3070,18 +3067,18 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
}
fn list_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EList<'a>> {
map_with_arena!(
collection_trailing_sep_e!(
map_with_arena(
collection_trailing_sep_e(
byte(b'[', EList::Open),
specialize_err_ref(EList::Expr, loc_expr(false)),
byte(b',', EList::End),
byte(b']', EList::End),
Expr::SpaceBefore
Expr::SpaceBefore,
),
|arena, elements: Collection<'a, _>| {
let elements = elements.ptrify_items(arena);
Expr::List(elements)
}
},
)
.trace("list_literal")
}
@ -3201,19 +3198,19 @@ impl<'a> Spaceable<'a> for RecordField<'a> {
pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
use RecordField::*;
map_with_arena!(
and!(
specialize_err(|_, pos| ERecord::Field(pos), loc!(lowercase_ident())),
and!(
map_with_arena(
and(
specialize_err(|_, pos| ERecord::Field(pos), loc(lowercase_ident())),
and(
spaces(),
optional(either!(
and!(byte(b':', ERecord::Colon), record_field_expr()),
and!(
optional(either(
and(byte(b':', ERecord::Colon), record_field_expr()),
and(
byte(b'?', ERecord::QuestionMark),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false)))
)
))
)
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
),
)),
),
),
|arena: &'a bumpalo::Bump, (loc_label, (spaces, opt_loc_val))| {
match opt_loc_val {
@ -3239,7 +3236,7 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
}
}
}
}
},
)
}
@ -3249,38 +3246,36 @@ enum RecordFieldExpr<'a> {
}
fn record_field_expr<'a>() -> impl Parser<'a, RecordFieldExpr<'a>, ERecord<'a>> {
map_with_arena!(
and!(
map_with_arena(
and(
spaces(),
either!(
and!(
either(
and(
two_bytes(b'<', b'-', ERecord::Arrow),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false)))
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
),
specialize_err_ref(ERecord::Expr, loc_expr(false))
)
specialize_err_ref(ERecord::Expr, loc_expr(false)),
),
),
|arena: &'a bumpalo::Bump, (spaces, either)| {
match either {
Either::First((_, loc_expr)) => RecordFieldExpr::Apply(spaces, loc_expr),
Either::Second(loc_expr) => RecordFieldExpr::Value({
if spaces.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(spaces, loc_expr.region)
}
}),
}
}
|arena: &'a bumpalo::Bump, (spaces, either)| match either {
Either::First((_, loc_expr)) => RecordFieldExpr::Apply(spaces, loc_expr),
Either::Second(loc_expr) => RecordFieldExpr::Value({
if spaces.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(spaces, loc_expr.region)
}
}),
},
)
}
fn record_updateable_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> {
specialize_err(
|_, pos| ERecord::Updateable(pos),
map_with_arena!(parse_ident, ident_to_expr),
map_with_arena(parse_ident, ident_to_expr),
)
}
@ -3290,37 +3285,37 @@ struct RecordHelp<'a> {
}
fn record_help<'a>() -> impl Parser<'a, RecordHelp<'a>, ERecord<'a>> {
between!(
between(
byte(b'{', ERecord::Open),
reset_min_indent(record!(RecordHelp {
// You can optionally have an identifier followed by an '&' to
// make this a record update, e.g. { Foo.user & username: "blah" }.
update: optional(backtrackable(skip_second!(
update: optional(backtrackable(skip_second(
spaces_around(
// We wrap the ident in an Expr here,
// so that we have a Spaceable value to work with,
// and then in canonicalization verify that it's an Expr::Var
// (and not e.g. an `Expr::Access`) and extract its string.
loc!(record_updateable_identifier()),
loc(record_updateable_identifier()),
),
byte(b'&', ERecord::Ampersand)
))),
fields: collection_inner!(
loc!(record_field()),
fields: collection_inner(
loc(record_field()),
byte(b',', ERecord::End),
RecordField::SpaceBefore
),
})),
byte(b'}', ERecord::End)
byte(b'}', ERecord::End),
)
}
fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
then(
and!(
and(
specialize_err(EExpr::Record, record_help()),
// there can be field access, e.g. `{ x : 4 }.x`
record_field_access_chain()
record_field_access_chain(),
),
move |arena, state, _, (record, accessors)| {
let expr_result = match record.update {
@ -3412,7 +3407,7 @@ fn apply_expr_access_chain<'a>(
}
fn string_like_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
map_with_arena!(
map_with_arena(
crate::string_literal::parse_str_like_literal(),
|arena, lit| match lit {
StrLikeLiteral::Str(s) => Expr::Str(s),
@ -3420,12 +3415,12 @@ fn string_like_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
// TODO: preserve the original escaping
Expr::SingleQuote(s.to_str_in(arena))
}
}
},
)
}
fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
map!(
map(
crate::number_literal::positive_number_literal(),
|literal| {
use crate::number_literal::NumLiteral::*;
@ -3443,12 +3438,12 @@ fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
is_negative,
},
}
}
},
)
}
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
map!(crate::number_literal::number_literal(), |literal| {
map(crate::number_literal::number_literal(), |literal| {
use crate::number_literal::NumLiteral::*;
match literal {

View file

@ -4,7 +4,10 @@ use crate::ast::{
use crate::blankspace::space0_e;
use crate::expr::merge_spaces;
use crate::ident::{lowercase_ident, UppercaseIdent};
use crate::parser::{byte, specialize_err, EPackageEntry, EPackageName, Parser};
use crate::parser::{
and, byte, loc, map_with_arena, skip_first, skip_second, specialize_err, EPackageEntry,
EPackageName, Parser,
};
use crate::parser::{optional, then};
use crate::string_literal;
use roc_module::symbol::ModuleId;
@ -355,29 +358,29 @@ pub struct PackageEntry<'a> {
}
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
map_with_arena!(
map_with_arena(
// You may optionally have a package shorthand,
// e.g. "uc" in `uc: roc/unicode 1.0.0`
//
// (Indirect dependencies don't have a shorthand.)
and!(
optional(and!(
skip_second!(
and!(
and(
optional(and(
skip_second(
and(
specialize_err(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
space0_e(EPackageEntry::IndentPackage)
space0_e(EPackageEntry::IndentPackage),
),
byte(b':', EPackageEntry::Colon)
byte(b':', EPackageEntry::Colon),
),
space0_e(EPackageEntry::IndentPackage)
space0_e(EPackageEntry::IndentPackage),
)),
and!(
optional(skip_first!(
and(
optional(skip_first(
crate::parser::keyword(crate::keyword::PLATFORM, EPackageEntry::Platform),
space0_e(EPackageEntry::IndentPackage)
space0_e(EPackageEntry::IndentPackage),
)),
loc!(specialize_err(EPackageEntry::BadPackage, package_name()))
)
loc(specialize_err(EPackageEntry::BadPackage, package_name())),
),
),
move |arena, (opt_shorthand, (platform_marker, package_or_path))| {
let entry = match opt_shorthand {
@ -400,15 +403,15 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
};
Spaced::Item(entry)
}
},
)
}
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> {
then(
loc!(specialize_err(
loc(specialize_err(
EPackageName::BadPath,
string_literal::parse_str_literal()
string_literal::parse_str_literal(),
)),
move |_arena, state, progress, text| match text.value {
StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), state)),

View file

@ -11,9 +11,10 @@ use crate::header::{
use crate::ident::{self, lowercase_ident, unqualified_ident, UppercaseIdent};
use crate::parser::Progress::{self, *};
use crate::parser::{
backtrackable, byte, increment_min_indent, optional, reset_min_indent, specialize_err,
two_bytes, EExposes, EHeader, EImports, EPackages, EParams, EProvides, ERequires, ETypedIdent,
Parser, SourceError, SpaceProblem, SyntaxError,
and, backtrackable, byte, collection_trailing_sep_e, increment_min_indent, loc, map,
map_with_arena, optional, reset_min_indent, skip_first, skip_second, specialize_err, succeed,
two_bytes, zero_or_more, EExposes, EHeader, EImports, EPackages, EParams, EProvides, ERequires,
ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
};
use crate::pattern::record_pattern_fields;
use crate::state::State;
@ -63,43 +64,43 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
record!(Module {
comments: space0_e(EHeader::IndentStart),
header: one_of![
map!(
skip_first!(
map(
skip_first(
keyword("module", EHeader::Start),
increment_min_indent(module_header())
),
Header::Module
),
map!(
skip_first!(
map(
skip_first(
keyword("interface", EHeader::Start),
increment_min_indent(interface_header())
),
Header::Module
),
map!(
skip_first!(
map(
skip_first(
keyword("app", EHeader::Start),
increment_min_indent(one_of![app_header(), old_app_header()])
),
Header::App
),
map!(
skip_first!(
map(
skip_first(
keyword("package", EHeader::Start),
increment_min_indent(one_of![package_header(), old_package_header()])
),
Header::Package
),
map!(
skip_first!(
map(
skip_first(
keyword("platform", EHeader::Start),
increment_min_indent(platform_header())
),
Header::Platform
),
map!(
skip_first!(
map(
skip_first(
keyword("hosted", EHeader::Start),
increment_min_indent(hosted_header())
),
@ -115,7 +116,7 @@ fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
after_keyword: space0_e(EHeader::IndentStart),
params: optional(specialize_err(EHeader::Params, module_params())),
exposes: specialize_err(EHeader::Exposes, exposes_list()),
interface_imports: succeed!(None)
interface_imports: succeed(None)
})
.trace("module_header")
}
@ -123,14 +124,15 @@ fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
fn module_params<'a>() -> impl Parser<'a, ModuleParams<'a>, EParams<'a>> {
record!(ModuleParams {
params: specialize_err(EParams::Pattern, record_pattern_fields()),
before_arrow: skip_second!(
before_arrow: skip_second(
space0_e(EParams::BeforeArrow),
loc!(two_bytes(b'-', b'>', EParams::Arrow))
loc(two_bytes(b'-', b'>', EParams::Arrow))
),
after_arrow: space0_e(EParams::AfterArrow),
})
}
// TODO does this need to be a macro?
macro_rules! merge_n_spaces {
($arena:expr, $($slice:expr),*) => {
{
@ -144,25 +146,25 @@ macro_rules! merge_n_spaces {
/// Parse old interface headers so we can format them into module headers
#[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
let after_keyword = map_with_arena!(
and!(
skip_second!(
let after_keyword = map_with_arena(
and(
skip_second(
space0_e(EHeader::IndentStart),
loc!(module_name_help(EHeader::ModuleName))
loc(module_name_help(EHeader::ModuleName)),
),
specialize_err(EHeader::Exposes, exposes_kw())
specialize_err(EHeader::Exposes, exposes_kw()),
),
|arena: &'a bumpalo::Bump,
(before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| {
merge_n_spaces!(arena, before_name, kw.before, kw.after)
}
},
);
record!(ModuleHeader {
after_keyword: after_keyword,
params: succeed!(None),
params: succeed(None),
exposes: specialize_err(EHeader::Exposes, exposes_list()).trace("exposes_list"),
interface_imports: map!(
interface_imports: map(
specialize_err(EHeader::Imports, imports()),
imports_none_if_empty
)
@ -183,7 +185,7 @@ fn imports_none_if_empty(value: ImportsKeywordItem<'_>) -> Option<ImportsKeyword
fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
record!(HostedHeader {
before_name: space0_e(EHeader::IndentStart),
name: loc!(module_name_help(EHeader::ModuleName)),
name: loc(module_name_help(EHeader::ModuleName)),
exposes: specialize_err(EHeader::Exposes, exposes_values_kw()),
imports: specialize_err(EHeader::Imports, imports()),
})
@ -255,9 +257,9 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
before_provides: space0_e(EHeader::IndentStart),
provides: specialize_err(EHeader::Exposes, exposes_list()),
before_packages: space0_e(EHeader::IndentStart),
packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
old_imports: succeed!(None),
old_provides_to_new_package: succeed!(None),
packages: specialize_err(EHeader::Packages, loc(packages_collection())),
old_imports: succeed(None),
old_provides_to_new_package: succeed(None),
})
.trace("app_header")
}
@ -275,19 +277,19 @@ type OldAppPackages<'a> =
#[inline(always)]
fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
let old = record!(OldAppHeader {
before_name: skip_second!(
before_name: skip_second(
space0_e(EHeader::IndentStart),
loc!(crate::parser::specialize_err(
loc(crate::parser::specialize_err(
EHeader::AppName,
string_literal::parse_str_literal()
))
),
packages: optional(specialize_err(EHeader::Packages, loc!(packages()))),
packages: optional(specialize_err(EHeader::Packages, loc(packages()))),
imports: optional(specialize_err(EHeader::Imports, imports())),
provides: specialize_err(EHeader::Provides, provides_to()),
});
map_with_arena!(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| {
map_with_arena(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| {
let mut before_packages: &'a [CommentOrNewline] = &[];
let packages = match old.packages {
@ -392,7 +394,7 @@ fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
before_exposes: space0_e(EHeader::IndentStart),
exposes: specialize_err(EHeader::Exposes, exposes_module_collection()),
before_packages: space0_e(EHeader::IndentStart),
packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
packages: specialize_err(EHeader::Packages, loc(packages_collection())),
})
.trace("package_header")
}
@ -407,14 +409,14 @@ struct OldPackageHeader<'a> {
#[inline(always)]
fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
map_with_arena!(
map_with_arena(
record!(OldPackageHeader {
before_name: skip_second!(
before_name: skip_second(
space0_e(EHeader::IndentStart),
specialize_err(EHeader::PackageName, package_name())
),
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
packages: specialize_err(EHeader::Packages, loc!(packages())),
packages: specialize_err(EHeader::Packages, loc(packages())),
}),
|arena: &'a bumpalo::Bump, old: OldPackageHeader<'a>| {
let before_exposes = merge_n_spaces!(
@ -435,7 +437,7 @@ fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
before_packages,
packages: old.packages.map(|kw| kw.item),
}
}
},
)
.trace("old_package_header")
}
@ -444,7 +446,7 @@ fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
record!(PlatformHeader {
before_name: space0_e(EHeader::IndentStart),
name: loc!(specialize_err(EHeader::PlatformName, package_name())),
name: loc(specialize_err(EHeader::PlatformName, package_name())),
requires: specialize_err(EHeader::Requires, requires()),
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
packages: specialize_err(EHeader::Packages, packages()),
@ -458,9 +460,9 @@ fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
one_of![
specialize_err(
|_, pos| EProvides::Identifier(pos),
map!(lowercase_ident(), To::ExistingPackage)
map(lowercase_ident(), To::ExistingPackage)
),
specialize_err(EProvides::Package, map!(package_name(), To::NewPackage))
specialize_err(EProvides::Package, map(package_name(), To::NewPackage))
]
}
@ -473,7 +475,7 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> {
EProvides::IndentProvides,
EProvides::IndentListStart
),
entries: collection_trailing_sep_e!(
entries: collection_trailing_sep_e(
byte(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
@ -487,7 +489,7 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> {
EProvides::IndentTo,
EProvides::IndentListStart
),
to: loc!(provides_to_package()),
to: loc(provides_to_package()),
})
.trace("provides_to")
}
@ -504,7 +506,7 @@ fn provides_exposed<'a>() -> impl Parser<
EProvides::IndentProvides,
EProvides::IndentListStart
),
item: collection_trailing_sep_e!(
item: collection_trailing_sep_e(
byte(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
@ -517,25 +519,25 @@ fn provides_exposed<'a>() -> impl Parser<
#[inline(always)]
fn provides_types<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, EProvides<'a>> {
skip_first!(
skip_first(
// We only support spaces here, not newlines, because this is not intended
// to be the design forever. Someday it will hopefully work like Elm,
// where platform authors can provide functions like Browser.sandbox which
// present an API based on ordinary-looking type variables.
zero_or_more!(byte(
zero_or_more(byte(
b' ',
// HACK: If this errors, EProvides::Provides is not an accurate reflection
// of what went wrong. However, this is both skipped and zero_or_more,
// so this error should never be visible to anyone in practice!
EProvides::Provides
EProvides::Provides,
)),
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', EProvides::ListStart),
provides_type_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
byte(b'}', EProvides::ListEnd),
Spaced::SpaceBefore
)
Spaced::SpaceBefore,
),
)
}
@ -547,9 +549,9 @@ where
F: Copy,
E: 'a,
{
loc!(map!(
specialize_err(|_, pos| to_expectation(pos), ident::uppercase()),
Spaced::Item
loc(map(
specialize_err(move |_, pos| to_expectation(pos), ident::uppercase()),
Spaced::Item,
))
}
@ -561,9 +563,9 @@ where
F: Copy,
E: 'a,
{
loc!(map!(
specialize_err(|_, pos| to_expectation(pos), unqualified_ident()),
|n| Spaced::Item(ExposedName::new(n))
loc(map(
specialize_err(move |_, pos| to_expectation(pos), unqualified_ident()),
|n| Spaced::Item(ExposedName::new(n)),
))
}
@ -584,7 +586,7 @@ fn requires<'a>(
#[inline(always)]
fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> {
record!(PlatformRequires {
rigids: skip_second!(requires_rigids(), space0_e(ERequires::ListStart)),
rigids: skip_second(requires_rigids(), space0_e(ERequires::ListStart)),
signature: requires_typed_ident()
})
}
@ -592,30 +594,30 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a
#[inline(always)]
fn requires_rigids<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, ERequires<'a>> {
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', ERequires::ListStart),
specialize_err(
|_, pos| ERequires::Rigid(pos),
loc!(map!(ident::uppercase(), Spaced::Item))
loc(map(ident::uppercase(), Spaced::Item)),
),
byte(b',', ERequires::ListEnd),
byte(b'}', ERequires::ListEnd),
Spaced::SpaceBefore
Spaced::SpaceBefore,
)
}
#[inline(always)]
fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>> {
skip_first!(
skip_first(
byte(b'{', ERequires::ListStart),
skip_second!(
skip_second(
reset_min_indent(space0_around_ee(
specialize_err(ERequires::TypedIdent, loc!(typed_ident()),),
specialize_err(ERequires::TypedIdent, loc(typed_ident())),
ERequires::ListStart,
ERequires::ListEnd
ERequires::ListEnd,
)),
byte(b'}', ERequires::ListStart)
)
byte(b'}', ERequires::ListStart),
),
)
}
@ -644,12 +646,12 @@ fn exposes_kw<'a>() -> impl Parser<'a, Spaces<'a, ExposesKeyword>, EExposes> {
#[inline(always)]
fn exposes_list<'a>() -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>, EExposes>
{
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'[', EExposes::ListStart),
exposes_entry(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore
Spaced::SpaceBefore,
)
}
@ -662,21 +664,22 @@ pub fn spaces_around_keyword<'a, K: Keyword, E>(
where
E: 'a + SpaceProblem,
{
map!(
and!(
skip_second!(
map(
and(
skip_second(
// parse any leading space before the keyword
backtrackable(space0_e(indent_problem1)),
crate::parser::keyword(K::KEYWORD, expectation)
// parse the keyword
crate::parser::keyword(K::KEYWORD, expectation),
),
space0_e(indent_problem2)
// parse the trailing space
space0_e(indent_problem2),
),
|(before, after)| {
Spaces {
before,
item: keyword_item,
after,
}
}
move |(before, after)| Spaces {
before,
item: keyword_item,
after,
},
)
}
@ -699,12 +702,12 @@ fn exposes_modules<'a>() -> impl Parser<
fn exposes_module_collection<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>, EExposes> {
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'[', EExposes::ListStart),
exposes_module(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore
Spaced::SpaceBefore,
)
}
@ -716,9 +719,9 @@ where
F: Copy,
E: 'a,
{
loc!(map!(
specialize_err(|_, pos| to_expectation(pos), module_name()),
Spaced::Item
loc(map(
specialize_err(move |_, pos| to_expectation(pos), module_name()),
Spaced::Item,
))
}
@ -747,16 +750,15 @@ fn packages_kw<'a>() -> impl Parser<'a, Spaces<'a, PackagesKeyword>, EPackages<'
#[inline(always)]
fn packages_collection<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>, EPackages<'a>> {
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', EPackages::ListStart),
specialize_err(EPackages::PackageEntry, loc!(package_entry())),
specialize_err(EPackages::PackageEntry, loc(package_entry())),
byte(b',', EPackages::ListEnd),
byte(b'}', EPackages::ListEnd),
Spaced::SpaceBefore
Spaced::SpaceBefore,
)
}
#[inline(always)]
fn imports<'a>() -> impl Parser<
'a,
KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
@ -769,9 +771,9 @@ fn imports<'a>() -> impl Parser<
EImports::IndentImports,
EImports::IndentListStart
),
item: collection_trailing_sep_e!(
item: collection_trailing_sep_e(
byte(b'[', EImports::ListStart),
loc!(imports_entry()),
loc(imports_entry()),
byte(b',', EImports::ListEnd),
byte(b']', EImports::ListEnd),
Spaced::SpaceBefore
@ -785,25 +787,25 @@ pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedId
// e.g.
//
// printLine : Str -> Task {} *
map!(
and!(
and!(
loc!(specialize_err(
map(
and(
and(
loc(specialize_err(
|_, pos| ETypedIdent::Identifier(pos),
lowercase_ident()
lowercase_ident(),
)),
space0_e(ETypedIdent::IndentHasType)
space0_e(ETypedIdent::IndentHasType),
),
skip_first!(
skip_first(
byte(b':', ETypedIdent::HasType),
space0_before_e(
specialize_err(
ETypedIdent::Type,
reset_min_indent(type_annotation::located(true))
reset_min_indent(type_annotation::located(true)),
),
ETypedIdent::IndentType,
)
)
),
),
),
|((ident, spaces_before_colon), ann)| {
Spaced::Item(TypedIdent {
@ -811,7 +813,7 @@ pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedId
spaces_before_colon,
ann,
})
}
},
)
}
@ -835,12 +837,24 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
Option<Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
);
let spaced_import = |((opt_shortname, module_name), opt_values): Temp<'a>| {
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
let entry = match opt_shortname {
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),
None => ImportsEntry::Module(module_name, exposed_values),
};
Spaced::Item(entry)
};
one_of!(
map!(
and!(
and!(
map(
and(
and(
// e.g. `pf.`
optional(backtrackable(skip_second!(
optional(backtrackable(skip_second(
shortname(),
byte(b'.', EImports::ShorthandDot)
))),
@ -848,9 +862,9 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
module_name_help(EImports::ModuleName)
),
// e.g. `.{ Task, after}`
optional(skip_first!(
optional(skip_first(
byte(b'.', EImports::ExposingDot),
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', EImports::SetStart),
exposes_entry(EImports::Identifier),
byte(b',', EImports::SetEnd),
@ -859,30 +873,18 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
)
))
),
|((opt_shortname, module_name), opt_values): Temp<'a>| {
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
let entry = match opt_shortname {
Some(shortname) => {
ImportsEntry::Package(shortname, module_name, exposed_values)
}
None => ImportsEntry::Module(module_name, exposed_values),
};
Spaced::Item(entry)
}
spaced_import
)
.trace("normal_import"),
map!(
and!(
and!(
map(
and(
and(
// e.g. "filename"
// TODO: str literal allows for multiline strings. We probably don't want that for file names.
specialize_err(|_, pos| EImports::StrLiteral(pos), parse_str_literal()),
// e.g. as
and!(
and!(
and(
and(
space0_e(EImports::AsKeyword),
two_bytes(b'a', b's', EImports::AsKeyword)
),

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,12 @@ use crate::ast::{Collection, Implements, Pattern, PatternAs, Spaceable};
use crate::blankspace::{space0_e, spaces, spaces_before};
use crate::ident::{lowercase_ident, parse_ident, Accessor, Ident};
use crate::keyword;
use crate::parser::Progress::{self, *};
use crate::parser::{
self, backtrackable, byte, fail_when, optional, specialize_err, specialize_err_ref, then,
three_bytes, two_bytes, EPattern, PInParens, PList, PRecord, Parser,
self, backtrackable, byte, collection_trailing_sep_e, fail_when, loc, map, map_with_arena,
optional, skip_first, specialize_err, specialize_err_ref, then, three_bytes, two_bytes,
zero_or_more, EPattern, PInParens, PList, PRecord, Parser,
};
use crate::parser::{either, Progress::*};
use crate::state::State;
use crate::string_literal::StrLikeLiteral;
use bumpalo::collections::string::String;
@ -31,9 +32,9 @@ pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
// An ident is the most common param, e.g. \foo -> ...
loc_ident_pattern_help(true),
// Underscore is also common, e.g. \_ -> ...
loc!(underscore_pattern_help()),
loc(underscore_pattern_help()),
// You can destructure records in params, e.g. \{ x, y } -> ...
loc!(specialize_err(
loc(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
)),
@ -78,15 +79,15 @@ fn loc_pattern_help_help<'a>(
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
loc(underscore_pattern_help()),
loc_ident_pattern_help(can_have_arguments),
loc!(specialize_err(
loc(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
)),
loc!(specialize_err(EPattern::List, list_pattern_help())),
loc!(number_pattern_help()),
loc!(string_like_pattern_help()),
loc(specialize_err(EPattern::List, list_pattern_help())),
loc(number_pattern_help()),
loc(string_like_pattern_help()),
)
}
@ -100,7 +101,7 @@ fn pattern_as<'a>() -> impl Parser<'a, PatternAs<'a>, EPattern<'a>> {
let position = state.pos();
match loc!(lowercase_ident()).parse(arena, state, min_indent) {
match loc(lowercase_ident()).parse(arena, state, min_indent) {
Ok((_, identifier, state)) => Ok((
MadeProgress,
PatternAs {
@ -115,13 +116,13 @@ fn pattern_as<'a>() -> impl Parser<'a, PatternAs<'a>, EPattern<'a>> {
}
fn loc_tag_pattern_args_help<'a>() -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(false))
zero_or_more(loc_tag_pattern_arg(false))
}
/// Like `loc_tag_pattern_args_help`, but stops if a "implements" keyword is seen (indicating an ability).
fn loc_type_def_tag_pattern_args_help<'a>(
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(true))
zero_or_more(loc_tag_pattern_arg(true))
}
fn loc_tag_pattern_arg<'a>(
@ -190,12 +191,12 @@ pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPatt
fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
then(
loc!(collection_trailing_sep_e!(
loc(collection_trailing_sep_e(
byte(b'(', PInParens::Open),
specialize_err_ref(PInParens::Pattern, loc_pattern_help()),
byte(b',', PInParens::End),
byte(b')', PInParens::End),
Pattern::SpaceBefore
Pattern::SpaceBefore,
)),
move |_arena, state, _, loc_elements| {
let elements = loc_elements.value;
@ -223,7 +224,7 @@ fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInPare
fn number_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
specialize_err(
EPattern::NumLiteral,
map!(crate::number_literal::number_literal(), |literal| {
map(crate::number_literal::number_literal(), |literal| {
use crate::number_literal::NumLiteral::*;
match literal {
@ -246,7 +247,7 @@ fn number_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
fn string_like_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
specialize_err(
|_, pos| EPattern::Start(pos),
map_with_arena!(
map_with_arena(
crate::string_literal::parse_str_like_literal(),
|arena, lit| match lit {
StrLikeLiteral::Str(s) => Pattern::StrLiteral(s),
@ -254,21 +255,21 @@ fn string_like_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>>
// TODO: preserve the original escaping
Pattern::SingleQuote(s.to_str_in(arena))
}
}
},
),
)
}
fn list_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PList<'a>> {
map!(
collection_trailing_sep_e!(
map(
collection_trailing_sep_e(
byte(b'[', PList::Open),
list_element_pattern(),
byte(b',', PList::End),
byte(b']', PList::End),
Pattern::SpaceBefore
Pattern::SpaceBefore,
),
Pattern::List
Pattern::List,
)
}
@ -281,16 +282,13 @@ fn list_element_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
}
fn three_list_rest_pattern_error<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
fail_when(
PList::Rest,
loc!(three_bytes(b'.', b'.', b'.', PList::Rest)),
)
fail_when(PList::Rest, loc(three_bytes(b'.', b'.', b'.', PList::Rest)))
}
fn list_rest_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, loc_word, state) =
loc!(two_bytes(b'.', b'.', PList::Open)).parse(arena, state, min_indent)?;
loc(two_bytes(b'.', b'.', PList::Open)).parse(arena, state, min_indent)?;
let no_as = Loc::at(loc_word.region, Pattern::ListRest(None));
@ -325,9 +323,8 @@ fn loc_ident_pattern_help<'a>(
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let original_state = state.clone();
let (_, loc_ident, state) =
specialize_err(|_, pos| EPattern::Start(pos), loc!(parse_ident))
.parse(arena, state, min_indent)?;
let (_, loc_ident, state) = specialize_err(|_, pos| EPattern::Start(pos), loc(parse_ident))
.parse(arena, state, min_indent)?;
match loc_ident.value {
Ident::Tag(tag) => {
@ -450,15 +447,15 @@ fn loc_ident_pattern_help<'a>(
}
fn underscore_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
map!(
skip_first!(
map(
skip_first(
byte(b'_', EPattern::Underscore),
optional(lowercase_ident_pattern())
optional(lowercase_ident_pattern()),
),
|output| match output {
Some(name) => Pattern::Underscore(name),
None => Pattern::Underscore(""),
}
},
)
}
@ -468,17 +465,17 @@ fn lowercase_ident_pattern<'a>() -> impl Parser<'a, &'a str, EPattern<'a>> {
#[inline(always)]
fn record_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
map!(record_pattern_fields(), Pattern::RecordDestructure)
map(record_pattern_fields(), Pattern::RecordDestructure)
}
pub fn record_pattern_fields<'a>() -> impl Parser<'a, Collection<'a, Loc<Pattern<'a>>>, PRecord<'a>>
{
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', PRecord::Open),
record_pattern_field(),
byte(b',', PRecord::End),
byte(b'}', PRecord::End),
Pattern::SpaceBefore
Pattern::SpaceBefore,
)
}
@ -489,9 +486,9 @@ fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>>
// You must have a field name, e.g. "email"
// using the initial pos is important for error reporting
let pos = state.pos();
let (progress, loc_label, state) = loc!(specialize_err(
let (progress, loc_label, state) = loc(specialize_err(
move |_, _| PRecord::Field(pos),
lowercase_ident()
lowercase_ident(),
))
.parse(arena, state, min_indent)?;
debug_assert_eq!(progress, MadeProgress);
@ -500,9 +497,9 @@ fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>>
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.)
let (_, opt_loc_val, state) = optional(either!(
let (_, opt_loc_val, state) = optional(either(
byte(b':', PRecord::Colon),
byte(b'?', PRecord::Optional)
byte(b'?', PRecord::Optional),
))
.parse(arena, state, min_indent)?;

View file

@ -2,8 +2,8 @@ use crate::ast::{EscapedChar, SingleQuoteLiteral, StrLiteral, StrSegment};
use crate::expr;
use crate::parser::Progress::{self, *};
use crate::parser::{
allocated, byte, loc, reset_min_indent, specialize_err_ref, then, BadInputError, ESingleQuote,
EString, Parser,
allocated, between, byte, loc, reset_min_indent, skip_second, specialize_err_ref, then,
BadInputError, ESingleQuote, EString, Parser,
};
use crate::state::State;
use bumpalo::collections::vec::Vec;
@ -73,7 +73,7 @@ pub enum StrLikeLiteral<'a> {
pub fn parse_str_literal<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
then(
loc!(parse_str_like_literal()),
loc(parse_str_like_literal()),
|_arena, state, progress, str_like| match str_like.value {
StrLikeLiteral::SingleQuote(_) => Err((
progress,
@ -374,12 +374,12 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
// Parse an arbitrary expression, then give a
// canonicalization error if that expression variant
// is not allowed inside a string interpolation.
let (_progress, loc_expr, new_state) = skip_second!(
let (_progress, loc_expr, new_state) = skip_second(
specialize_err_ref(
EString::Format,
loc(allocated(reset_min_indent(expr::expr_help())))
loc(allocated(reset_min_indent(expr::expr_help()))),
),
byte(b')', EString::FormatEnd)
byte(b')', EString::FormatEnd),
)
.parse(arena, state, min_indent)?;
@ -403,10 +403,10 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
// Parse the hex digits, surrounded by parens, then
// give a canonicalization error if the digits form
// an invalid unicode code point.
let (_progress, loc_digits, new_state) = between!(
let (_progress, loc_digits, new_state) = between(
byte(b'(', EString::CodePtOpen),
loc(ascii_hex_digits()),
byte(b')', EString::CodePtEnd)
byte(b')', EString::CodePtEnd),
)
.parse(arena, state, min_indent)?;
@ -483,12 +483,12 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
let original_byte_count = state.bytes().len();
// Parse an arbitrary expression, followed by ')'
let (_progress, loc_expr, new_state) = skip_second!(
let (_progress, loc_expr, new_state) = skip_second(
specialize_err_ref(
EString::Format,
loc(allocated(reset_min_indent(expr::expr_help())))
loc(allocated(reset_min_indent(expr::expr_help()))),
),
byte(b')', EString::FormatEnd)
byte(b')', EString::FormatEnd),
)
.parse(arena, state, min_indent)?;

View file

@ -9,12 +9,14 @@ use crate::expr::{record_field, FoundApplyValue};
use crate::ident::{lowercase_ident, lowercase_ident_keyword_e};
use crate::keyword;
use crate::parser::{
absolute_column_min_indent, increment_min_indent, then, ERecord, ETypeAbilityImpl,
absolute_column_min_indent, and, collection_trailing_sep_e, either, increment_min_indent,
indented_seq, loc, map, map_with_arena, skip_first, skip_second, succeed, then, zero_or_more,
ERecord, ETypeAbilityImpl,
};
use crate::parser::{
allocated, backtrackable, byte, fail, optional, specialize_err, specialize_err_ref, two_bytes,
word, EType, ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, Parser,
Progress::{self, *},
Progress::*,
};
use crate::state::State;
use bumpalo::collections::vec::Vec;
@ -38,12 +40,12 @@ fn tag_union_type<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeTagUnion<'a>> {
move |arena, state, min_indent| {
let (_, tags, state) = collection_trailing_sep_e!(
let (_, tags, state) = collection_trailing_sep_e(
byte(b'[', ETypeTagUnion::Open),
loc!(tag_type(false)),
loc(tag_type(false)),
byte(b',', ETypeTagUnion::End),
byte(b']', ETypeTagUnion::End),
Tag::SpaceBefore
Tag::SpaceBefore,
)
.parse(arena, state, min_indent)?;
@ -109,29 +111,29 @@ fn parse_type_alias_after_as<'a>() -> impl Parser<'a, TypeHeader<'a>, EType<'a>>
}
fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map_with_arena!(
and!(
map_with_arena(
and(
one_of!(
loc_wildcard(),
loc_inferred(),
specialize_err(EType::TInParens, loc_type_in_parens(stop_at_surface_has)),
loc!(specialize_err(
loc(specialize_err(
EType::TRecord,
record_type(stop_at_surface_has)
)),
loc!(specialize_err(
loc(specialize_err(
EType::TTagUnion,
tag_union_type(stop_at_surface_has)
)),
loc!(applied_type(stop_at_surface_has)),
loc!(parse_type_variable(stop_at_surface_has)),
loc(applied_type(stop_at_surface_has)),
loc(parse_type_variable(stop_at_surface_has)),
fail(EType::TStart),
),
// Inline alias notation, e.g. [Nil, Cons a (List a)] as List a
one_of![
map!(
and!(
skip_second!(
map(
and(
skip_second(
backtrackable(space0_e(EType::TIndentEnd)),
crate::parser::keyword(keyword::AS, EType::TEnd)
),
@ -139,8 +141,8 @@ fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>
),
Some
),
succeed!(None)
]
succeed(None)
],
),
|arena: &'a Bump,
(loc_ann, opt_as): (Loc<TypeAnnotation<'a>>, Option<(&'a [_], TypeHeader<'a>)>)| {
@ -156,14 +158,14 @@ fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>
None => loc_ann,
}
}
},
)
.trace("type_annotation:term")
}
/// The `*` type variable, e.g. in (List *) Wildcard,
fn loc_wildcard<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map!(loc!(byte(b'*', EType::TWildcard)), |loc_val: Loc<()>| {
map(loc(byte(b'*', EType::TWildcard)), |loc_val: Loc<()>| {
loc_val.map(|_| TypeAnnotation::Wildcard)
})
}
@ -201,24 +203,24 @@ fn loc_inferred<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
fn loc_applied_arg<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map_with_arena!(
and!(
map_with_arena(
and(
backtrackable(space0_e(EType::TIndentStart)),
one_of!(
loc_wildcard(),
loc_inferred(),
specialize_err(EType::TInParens, loc_type_in_parens(stop_at_surface_has)),
loc!(specialize_err(
loc(specialize_err(
EType::TRecord,
record_type(stop_at_surface_has)
)),
loc!(specialize_err(
loc(specialize_err(
EType::TTagUnion,
tag_union_type(stop_at_surface_has)
)),
loc!(specialize_err(EType::TApply, concrete_type())),
loc!(parse_type_variable(stop_at_surface_has))
)
loc(specialize_err(EType::TApply, concrete_type())),
loc(parse_type_variable(stop_at_surface_has))
),
),
|arena: &'a Bump, (spaces, argument): (&'a [_], Loc<TypeAnnotation<'a>>)| {
if spaces.is_empty() {
@ -227,7 +229,7 @@ fn loc_applied_arg<'a>(
let Loc { region, value } = argument;
arena.alloc(value).with_spaces_before(spaces, region)
}
}
},
)
}
@ -235,18 +237,18 @@ fn loc_type_in_parens<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, ETypeInParens<'a>> {
then(
loc!(and!(
collection_trailing_sep_e!(
loc(and(
collection_trailing_sep_e(
byte(b'(', ETypeInParens::Open),
specialize_err_ref(ETypeInParens::Type, expression(true, false)),
byte(b',', ETypeInParens::End),
byte(b')', ETypeInParens::End),
TypeAnnotation::SpaceBefore
TypeAnnotation::SpaceBefore,
),
optional(allocated(specialize_err_ref(
ETypeInParens::Type,
term(stop_at_surface_has)
)))
term(stop_at_surface_has),
))),
)),
|_arena, state, progress, item| {
let Loc {
@ -274,7 +276,7 @@ fn loc_type_in_parens<'a>(
fn tag_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>> {
move |arena, state: State<'a>, min_indent: u32| {
let (_, name, state) =
loc!(parse_tag_name(ETypeTagUnion::End)).parse(arena, state, min_indent)?;
loc(parse_tag_name(ETypeTagUnion::End)).parse(arena, state, min_indent)?;
let (_, args, state) =
specialize_err_ref(ETypeTagUnion::Type, loc_applied_args_e(stop_at_surface_has))
@ -313,9 +315,9 @@ fn record_type_field<'a>() -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'
// You must have a field name, e.g. "email"
// using the initial pos is important for error reporting
let pos = state.pos();
let (progress, loc_label, state) = loc!(specialize_err(
let (progress, loc_label, state) = loc(specialize_err(
move |_, _| ETypeRecord::Field(pos),
lowercase_ident_keyword_e()
lowercase_ident_keyword_e(),
))
.parse(arena, state, min_indent)?;
debug_assert_eq!(progress, MadeProgress);
@ -325,9 +327,9 @@ fn record_type_field<'a>() -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.)
let (_, opt_loc_val, state) = optional(either!(
let (_, opt_loc_val, state) = optional(either(
byte(b':', ETypeRecord::Colon),
byte(b'?', ETypeRecord::Optional)
byte(b'?', ETypeRecord::Optional),
))
.parse(arena, state, min_indent)?;
@ -375,9 +377,9 @@ fn record_type<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
record!(TypeAnnotation::Record {
fields: collection_trailing_sep_e!(
fields: collection_trailing_sep_e(
byte(b'{', ETypeRecord::Open),
loc!(record_type_field()),
loc(record_type_field()),
byte(b',', ETypeRecord::End),
byte(b'}', ETypeRecord::End),
AssignedField::SpaceBefore
@ -391,12 +393,12 @@ fn record_type<'a>(
}
fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
map!(
indented_seq!(
map(
indented_seq(
specialize_err(EType::TApply, concrete_type()),
// Optionally parse space-separated arguments for the constructor,
// e.g. `Str Float` in `Map Str Float`
loc_applied_args_e(stop_at_surface_has)
loc_applied_args_e(stop_at_surface_has),
),
|(ctor, args): (TypeAnnotation<'a>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {
match &ctor {
@ -411,7 +413,7 @@ fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation
TypeAnnotation::Malformed(_) => ctor,
_ => unreachable!(),
}
}
},
)
.trace("type_annotation:applied_type")
}
@ -419,56 +421,56 @@ fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation
fn loc_applied_args_e<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
zero_or_more!(loc_applied_arg(stop_at_surface_has))
zero_or_more(loc_applied_arg(stop_at_surface_has))
}
// Hash & Eq & ...
fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
map!(
and!(
map(
and(
space0_before_optional_after(
specialize_err(EType::TApply, loc!(concrete_type())),
specialize_err(EType::TApply, loc(concrete_type())),
EType::TIndentStart,
EType::TIndentEnd,
),
zero_or_more!(skip_first!(
zero_or_more(skip_first(
byte(b'&', EType::TImplementsClause),
space0_before_optional_after(
specialize_err(EType::TApply, loc!(concrete_type())),
specialize_err(EType::TApply, loc(concrete_type())),
EType::TIndentStart,
EType::TIndentEnd,
)
))
),
)),
),
|(first_ability, mut other_abilities): (
Loc<TypeAnnotation<'a>>,
Vec<'a, Loc<TypeAnnotation<'a>>>
Vec<'a, Loc<TypeAnnotation<'a>>>,
)| {
other_abilities.insert(0, first_ability);
other_abilities
}
},
)
}
fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'a>> {
map!(
map(
// Suppose we are trying to parse "a implements Hash"
and!(
and(
space0_around_ee(
// Parse "a", with appropriate spaces
specialize_err(
|_, pos| EType::TBadTypeVariable(pos),
loc!(map!(lowercase_ident(), Spaced::Item)),
loc(map(lowercase_ident(), Spaced::Item)),
),
EType::TIndentStart,
EType::TIndentEnd
EType::TIndentEnd,
),
skip_first!(
skip_first(
// Parse "implements"; we don't care about this keyword
word(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
// Parse "Hash & ..."; this may be qualified from another module like "Hash.Hash"
absolute_column_min_indent(ability_chain())
)
absolute_column_min_indent(ability_chain()),
),
),
|(var, abilities): (Loc<Spaced<'a, &'a str>>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {
let abilities_region = Region::span_across(
@ -481,7 +483,7 @@ fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'
abilities: abilities.into_bump_slice(),
};
Loc::at(region, implements_clause)
}
},
)
}
@ -490,18 +492,18 @@ fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'
fn implements_clause_chain<'a>(
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [Loc<ImplementsClause<'a>>]), EType<'a>> {
move |arena, state: State<'a>, min_indent: u32| {
let (_, (spaces_before, ()), state) = and!(
let (_, (spaces_before, ()), state) = and(
space0_e(EType::TIndentStart),
word(crate::keyword::WHERE, EType::TWhereBar)
word(crate::keyword::WHERE, EType::TWhereBar),
)
.parse(arena, state, min_indent)?;
// Parse the first clause (there must be one), then the rest
let (_, first_clause, state) = implements_clause().parse(arena, state, min_indent)?;
let (_, mut clauses, state) = zero_or_more!(skip_first!(
let (_, mut clauses, state) = zero_or_more(skip_first(
byte(b',', EType::TImplementsClause),
implements_clause()
implements_clause(),
))
.parse(arena, state, min_indent)?;
@ -518,36 +520,36 @@ fn implements_clause_chain<'a>(
/// Parse a implements-abilities clause, e.g. `implements [Eq, Hash]`.
pub fn implements_abilities<'a>() -> impl Parser<'a, Loc<ImplementsAbilities<'a>>, EType<'a>> {
increment_min_indent(skip_first!(
increment_min_indent(skip_first(
// Parse "implements"; we don't care about this keyword
word(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
// Parse "Hash"; this may be qualified from another module like "Hash.Hash"
space0_before_e(
loc!(map!(
collection_trailing_sep_e!(
loc(map(
collection_trailing_sep_e(
byte(b'[', EType::TStart),
loc!(parse_implements_ability()),
loc(parse_implements_ability()),
byte(b',', EType::TEnd),
byte(b']', EType::TEnd),
ImplementsAbility::SpaceBefore
ImplementsAbility::SpaceBefore,
),
ImplementsAbilities::Implements
ImplementsAbilities::Implements,
)),
EType::TIndentEnd,
)
),
))
}
fn parse_implements_ability<'a>() -> impl Parser<'a, ImplementsAbility<'a>, EType<'a>> {
increment_min_indent(record!(ImplementsAbility::ImplementsAbility {
ability: loc!(specialize_err(EType::TApply, concrete_type())),
ability: loc(specialize_err(EType::TApply, concrete_type())),
impls: optional(backtrackable(space0_before_e(
loc!(map!(
loc(map(
specialize_err(
EType::TAbilityImpl,
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', ETypeAbilityImpl::Open),
specialize_err(|e: ERecord<'_>, _| e.into(), loc!(ability_impl_field())),
specialize_err(|e: ERecord<'_>, _| e.into(), loc(ability_impl_field())),
byte(b',', ETypeAbilityImpl::End),
byte(b'}', ETypeAbilityImpl::End),
AssignedField::SpaceBefore
@ -577,8 +579,8 @@ fn expression<'a>(
let (p1, first, state) = space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
.parse(arena, state, min_indent)?;
let result = and![
zero_or_more!(skip_first!(
let result = and(
zero_or_more(skip_first(
byte(b',', EType::TFunctionArgument),
one_of![
space0_around_ee(
@ -587,15 +589,15 @@ fn expression<'a>(
EType::TIndentEnd
),
fail(EType::TFunctionArgument)
]
],
))
.trace("type_annotation:expression:rest_args"),
skip_second!(
skip_second(
space0_e(EType::TIndentStart),
two_bytes(b'-', b'>', EType::TStart)
two_bytes(b'-', b'>', EType::TStart),
)
.trace("type_annotation:expression:arrow")
]
.trace("type_annotation:expression:arrow"),
)
.parse(arena, state.clone(), min_indent);
let (progress, annot, state) = match result {
@ -629,9 +631,9 @@ fn expression<'a>(
}
Err(err) => {
if !is_trailing_comma_valid {
let (_, comma, _) = optional(backtrackable(skip_first!(
let (_, comma, _) = optional(backtrackable(skip_first(
space0_e(EType::TIndentStart),
byte(b',', EType::TStart)
byte(b',', EType::TStart),
)))
.trace("check trailing comma")
.parse(arena, state.clone(), min_indent)?;

View file

@ -4,8 +4,7 @@ Ayaz Hafiz
## Summary
This document describes how polymorphic lambda sets are specialized and resolved
in the compiler's type solver. It's derived from the original document at <https://rwx.notion.site/Ambient-Lambda-Set-Specialization-50e0208a39844ad096626f4143a6394e>.
This document describes how polymorphic lambda sets are specialized and resolved in the compiler's type solver.
TL;DR: lambda sets are resolved by unifying their ambient arrow types in a “bottom-up” fashion.

View file

@ -2446,7 +2446,7 @@ fn pattern_as_of_symbol() {
#[mono_test]
fn function_specialization_information_in_lambda_set_thunk() {
// https://github.com/roc-lang/roc/issues/4734
// https://rwx.notion.site/Let-generalization-Let-s-not-742a3ab23ff742619129dcc848a271cf#6b08b0a203fb443db2d7238a0eb154eb
// https://github.com/roc-lang/rfcs/blob/main/0010-let-generalization-lets-not.md
indoc!(
r#"
app "test" provides [main] to "./platform"
@ -2465,7 +2465,7 @@ fn function_specialization_information_in_lambda_set_thunk() {
#[mono_test]
fn function_specialization_information_in_lambda_set_thunk_independent_defs() {
// https://github.com/roc-lang/roc/issues/4734
// https://rwx.notion.site/Let-generalization-Let-s-not-742a3ab23ff742619129dcc848a271cf#6b08b0a203fb443db2d7238a0eb154eb
// https://github.com/roc-lang/rfcs/blob/main/0010-let-generalization-lets-not.md
indoc!(
r#"
app "test" provides [main] to "./platform"