mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Merge branch 'main' into builtin-task
This commit is contained in:
commit
cd488300fd
24 changed files with 964 additions and 953 deletions
|
@ -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(
|
||||
|
|
10
crates/cli/tests/fixtures/transitive-deps/direct-one-and-two.roc
vendored
Normal file
10
crates/cli/tests/fixtures/transitive-deps/direct-one-and-two.roc
vendored
Normal 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)"
|
8
crates/cli/tests/fixtures/transitive-deps/direct-one.roc
vendored
Normal file
8
crates/cli/tests/fixtures/transitive-deps/direct-one.roc
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
app [main] {
|
||||
pf: platform "../packages/platform/main.roc",
|
||||
one: "one/main.roc",
|
||||
}
|
||||
|
||||
import one.One
|
||||
|
||||
main = One.example
|
8
crates/cli/tests/fixtures/transitive-deps/direct-zero.roc
vendored
Normal file
8
crates/cli/tests/fixtures/transitive-deps/direct-zero.roc
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
app [main] {
|
||||
pf: platform "../packages/platform/main.roc",
|
||||
zero: "zero/main.roc",
|
||||
}
|
||||
|
||||
import zero.Zero
|
||||
|
||||
main = Zero.example
|
5
crates/cli/tests/fixtures/transitive-deps/one/One.roc
vendored
Normal file
5
crates/cli/tests/fixtures/transitive-deps/one/One.roc
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
module [example]
|
||||
|
||||
import two.Two
|
||||
|
||||
example = "[One imports Two: $(Two.example)]"
|
3
crates/cli/tests/fixtures/transitive-deps/one/main.roc
vendored
Normal file
3
crates/cli/tests/fixtures/transitive-deps/one/main.roc
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
package [One] {
|
||||
two: "../two/main.roc",
|
||||
}
|
3
crates/cli/tests/fixtures/transitive-deps/two/Two.roc
vendored
Normal file
3
crates/cli/tests/fixtures/transitive-deps/two/Two.roc
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
module [example]
|
||||
|
||||
example = "From two"
|
1
crates/cli/tests/fixtures/transitive-deps/two/main.roc
vendored
Normal file
1
crates/cli/tests/fixtures/transitive-deps/two/main.roc
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package [Two] {}
|
5
crates/cli/tests/fixtures/transitive-deps/zero/Zero.roc
vendored
Normal file
5
crates/cli/tests/fixtures/transitive-deps/zero/Zero.roc
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
module [example]
|
||||
|
||||
import one.One
|
||||
|
||||
example = "[Zero imports One: $(One.example)]"
|
3
crates/cli/tests/fixtures/transitive-deps/zero/main.roc
vendored
Normal file
3
crates/cli/tests/fixtures/transitive-deps/zero/main.roc
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
package [Zero] {
|
||||
one: "../one/main.roc"
|
||||
}
|
|
@ -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)]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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
|
@ -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)?;
|
||||
|
||||
|
|
|
@ -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)?;
|
||||
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue