mirror of
https://github.com/roc-lang/roc.git
synced 2024-10-04 22:27:55 +03:00
Merge pull request #6634 from roc-lang/suffixed-bang
De-sugar bang `!` operator
This commit is contained in:
commit
c74685f74a
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2329,6 +2329,7 @@ dependencies = [
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_serialize",
|
||||
"roc_test_utils",
|
||||
"roc_types",
|
||||
"static_assertions",
|
||||
"ven_pretty",
|
||||
@ -2531,6 +2532,7 @@ version = "0.0.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_region",
|
||||
|
@ -17,6 +17,7 @@ roc_problem = { path = "../problem" }
|
||||
roc_region = { path = "../region" }
|
||||
roc_serialize = { path = "../serialize" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_test_utils = { path = "../../test_utils" }
|
||||
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
|
||||
|
@ -748,9 +748,10 @@ fn can_annotation_help(
|
||||
|
||||
for loc_var in *loc_vars {
|
||||
let var = match loc_var.value {
|
||||
Pattern::Identifier(name) if name.chars().next().unwrap().is_lowercase() => {
|
||||
name
|
||||
}
|
||||
Pattern::Identifier {
|
||||
ident: name,
|
||||
suffixed: _,
|
||||
} if name.chars().next().unwrap().is_lowercase() => name,
|
||||
_ => unreachable!("I thought this was validated during parsing"),
|
||||
};
|
||||
let var_name = Lowercase::from(var);
|
||||
|
@ -547,7 +547,11 @@ fn canonicalize_claimed_ability_impl<'a>(
|
||||
}
|
||||
AssignedField::RequiredValue(label, _spaces, value) => {
|
||||
let impl_ident = match value.value {
|
||||
ast::Expr::Var { module_name, ident } => {
|
||||
ast::Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: _,
|
||||
} => {
|
||||
if module_name.is_empty() {
|
||||
ident
|
||||
} else {
|
||||
@ -2570,9 +2574,10 @@ fn to_pending_alias_or_opaque<'a>(
|
||||
|
||||
for loc_var in vars.iter() {
|
||||
match loc_var.value {
|
||||
ast::Pattern::Identifier(name)
|
||||
if name.chars().next().unwrap().is_lowercase() =>
|
||||
{
|
||||
ast::Pattern::Identifier {
|
||||
ident: name,
|
||||
suffixed: _,
|
||||
} if name.chars().next().unwrap().is_lowercase() => {
|
||||
let lowercase = Lowercase::from(name);
|
||||
can_rigids.push(Loc {
|
||||
value: lowercase,
|
||||
@ -2874,6 +2879,8 @@ fn to_pending_value_def<'a>(
|
||||
condition,
|
||||
preceding_comment: *preceding_comment,
|
||||
}),
|
||||
|
||||
Stmt(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,13 @@ fn to_encoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
let opaque_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
|
||||
let opaque_apply_pattern = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
|
||||
// Encode.toEncoder payload
|
||||
@ -33,10 +37,12 @@ fn to_encoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Encode",
|
||||
ident: "toEncoder",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
})]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
));
|
||||
@ -61,19 +67,23 @@ fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "decodeWith",
|
||||
suffixed: 0,
|
||||
}),
|
||||
env.arena.alloc([
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: bytes,
|
||||
suffixed: 0,
|
||||
}),
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "decoder",
|
||||
suffixed: 0,
|
||||
}),
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: fmt,
|
||||
suffixed: 0,
|
||||
}),
|
||||
]),
|
||||
CalledVia::Space,
|
||||
@ -84,6 +94,7 @@ fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "mapResult",
|
||||
suffixed: 0,
|
||||
}),
|
||||
env.arena.alloc([
|
||||
&*alloc_expr(call_decode_with),
|
||||
@ -96,8 +107,20 @@ fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
// Decode.mapResult (Decode.decodeWith bytes Decode.decoder fmt) @Opaq
|
||||
let custom_closure = ast::Expr::Closure(
|
||||
env.arena.alloc([
|
||||
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(bytes)),
|
||||
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(fmt)),
|
||||
Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: bytes,
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: fmt,
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
]),
|
||||
alloc_expr(call_map_result),
|
||||
);
|
||||
@ -107,6 +130,7 @@ fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "custom",
|
||||
suffixed: 0,
|
||||
}),
|
||||
env.arena.alloc([&*alloc_expr(custom_closure)]),
|
||||
CalledVia::Space,
|
||||
@ -127,9 +151,13 @@ fn hash<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
let opaque_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
|
||||
let opaque_apply_pattern = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
|
||||
// Hash.hash hasher payload
|
||||
@ -137,15 +165,18 @@ fn hash<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Hash",
|
||||
ident: "hash",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: hasher,
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
}),
|
||||
]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
@ -154,7 +185,13 @@ fn hash<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
// \hasher, @Opaq payload -> Hash.hash hasher payload
|
||||
ast::Expr::Closure(
|
||||
env.arena.alloc([
|
||||
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(hasher)),
|
||||
Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: hasher,
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
Loc::at(DERIVED_REGION, opaque_apply_pattern),
|
||||
]),
|
||||
call_member,
|
||||
@ -172,16 +209,24 @@ fn is_eq<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
// \@Opaq payload1
|
||||
let opaque1 = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload1))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload1,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
// \@Opaq payload2
|
||||
let opaque2 = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload2))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload2,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
|
||||
// Bool.isEq payload1 payload2
|
||||
@ -189,15 +234,18 @@ fn is_eq<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Bool",
|
||||
ident: "isEq",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload1,
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload2,
|
||||
suffixed: 0,
|
||||
}),
|
||||
]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
@ -224,9 +272,13 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
let opaque_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
|
||||
let opaque_apply_pattern = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
|
||||
// Inspect.toInspector payload
|
||||
@ -234,10 +286,12 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Inspect",
|
||||
ident: "toInspector",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
})]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
));
|
||||
@ -252,6 +306,7 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Inspect",
|
||||
ident: "tag",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([&*opaque_name, &*to_inspector_list]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
@ -264,20 +319,27 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Inspect",
|
||||
ident: "apply",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([
|
||||
&*opaque_inspector,
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: fmt,
|
||||
suffixed: 0,
|
||||
}),
|
||||
]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
));
|
||||
|
||||
let custom_closure = alloc_expr(ast::Expr::Closure(
|
||||
env.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(fmt))]),
|
||||
env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: fmt,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
apply_opaque_inspector,
|
||||
));
|
||||
|
||||
@ -286,6 +348,7 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Inspect",
|
||||
ident: "custom",
|
||||
suffixed: 0,
|
||||
}),
|
||||
env.arena.alloc([&*custom_closure]),
|
||||
CalledVia::Space,
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![allow(clippy::manual_map)]
|
||||
|
||||
use crate::suffixed::{apply_task_await, unwrap_suffixed_expression, EUnwrapped};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_error_macros::internal_error;
|
||||
@ -56,7 +57,11 @@ fn new_op_call_expr<'a>(
|
||||
let args = arena.alloc([left, right]);
|
||||
|
||||
let loc_expr = arena.alloc(Loc {
|
||||
value: Expr::Var { module_name, ident },
|
||||
value: Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: 0,
|
||||
},
|
||||
region: loc_op.region,
|
||||
});
|
||||
|
||||
@ -92,9 +97,10 @@ fn desugar_value_def<'a>(
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment: *comment,
|
||||
body_pattern,
|
||||
body_pattern: desugar_loc_pattern(arena, body_pattern, src, line_info, module_path),
|
||||
body_expr: desugar_expr(arena, body_expr, src, line_info, module_path),
|
||||
},
|
||||
|
||||
Dbg {
|
||||
condition,
|
||||
preceding_comment,
|
||||
@ -128,6 +134,18 @@ fn desugar_value_def<'a>(
|
||||
preceding_comment: *preceding_comment,
|
||||
}
|
||||
}
|
||||
|
||||
Stmt(stmt_expr) => {
|
||||
// desugar into a Body({}, stmt_expr)
|
||||
let loc_pattern = arena.alloc(Loc::at(
|
||||
stmt_expr.region,
|
||||
Pattern::RecordDestructure(Collection::empty()),
|
||||
));
|
||||
Body(
|
||||
loc_pattern,
|
||||
desugar_expr(arena, stmt_expr, src, line_info, module_path),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,227 +155,119 @@ pub fn desugar_defs_node_values<'a>(
|
||||
src: &'a str,
|
||||
line_info: &mut Option<LineInfo>,
|
||||
module_path: &str,
|
||||
top_level_def: bool,
|
||||
) {
|
||||
for value_def in defs.value_defs.iter_mut() {
|
||||
*value_def = desugar_value_def(arena, arena.alloc(*value_def), src, line_info, module_path);
|
||||
}
|
||||
}
|
||||
|
||||
fn desugar_defs_node_suffixed<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
) -> &'a Loc<Expr<'a>> {
|
||||
match loc_expr.value {
|
||||
Defs(defs, loc_ret) => {
|
||||
match defs.search_suffixed_defs() {
|
||||
None => loc_expr,
|
||||
Some((tag_index, value_index)) => {
|
||||
if defs.value_defs.len() == 1 {
|
||||
// We have only one value_def and it must be Suffixed
|
||||
// replace Defs with an Apply(Task.await) and Closure of loc_return
|
||||
|
||||
debug_assert!(
|
||||
value_index == 0,
|
||||
"we have only one value_def and so it must be Suffixed "
|
||||
);
|
||||
|
||||
// Unwrap Suffixed def within Apply, and the pattern so we can use in the call to Task.await
|
||||
let (suffixed_sub_apply_loc, pattern) = unwrap_suffixed_def_and_pattern(
|
||||
arena,
|
||||
loc_expr.region,
|
||||
defs.value_defs[0],
|
||||
);
|
||||
|
||||
// Create Closure for the result of the recursion,
|
||||
// use the pattern from our Suffixed Def as closure argument
|
||||
let closure_expr = Closure(arena.alloc([*pattern]), loc_ret);
|
||||
|
||||
// Apply arguments to Task.await, first is the unwrapped Suffix expr second is the Closure
|
||||
let mut task_await_apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
|
||||
|
||||
task_await_apply_args
|
||||
.push(arena.alloc(Loc::at(loc_expr.region, suffixed_sub_apply_loc)));
|
||||
task_await_apply_args
|
||||
.push(arena.alloc(Loc::at(loc_expr.region, closure_expr)));
|
||||
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Apply(
|
||||
arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "await",
|
||||
},
|
||||
}),
|
||||
arena.alloc(task_await_apply_args),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
))
|
||||
} else if value_index == 0 {
|
||||
// We have a Suffixed in first index, and also other nodes in Defs
|
||||
// pop the first Suffixed and recurse on Defs (without first) to handle any other Suffixed
|
||||
// the result will be wrapped in an Apply(Task.await) and Closure
|
||||
|
||||
debug_assert!(
|
||||
defs.value_defs.len() > 1,
|
||||
"we know we have other Defs that will need to be considered"
|
||||
);
|
||||
|
||||
// Unwrap Suffixed def within Apply, and the pattern so we can use in the call to Task.await
|
||||
let (suffixed_sub_apply_loc, pattern) = unwrap_suffixed_def_and_pattern(
|
||||
arena,
|
||||
loc_expr.region,
|
||||
defs.value_defs[0],
|
||||
);
|
||||
|
||||
// Get a mutable copy of the defs
|
||||
let mut copied_defs = defs.clone();
|
||||
|
||||
// Remove the suffixed def
|
||||
copied_defs.remove_value_def(tag_index);
|
||||
|
||||
// Recurse using new Defs to get new expression
|
||||
let new_loc_expr = desugar_defs_node_suffixed(
|
||||
arena,
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Defs(arena.alloc(copied_defs), loc_ret),
|
||||
)),
|
||||
);
|
||||
|
||||
// Create Closure for the result of the recursion,
|
||||
// use the pattern from our Suffixed Def as closure argument
|
||||
let closure_expr = Closure(arena.alloc([*pattern]), new_loc_expr);
|
||||
|
||||
// Apply arguments to Task.await, first is the unwrapped Suffix expr second is the Closure
|
||||
let mut task_await_apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
|
||||
|
||||
task_await_apply_args
|
||||
.push(arena.alloc(Loc::at(loc_expr.region, suffixed_sub_apply_loc)));
|
||||
task_await_apply_args
|
||||
.push(arena.alloc(Loc::at(loc_expr.region, closure_expr)));
|
||||
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Apply(
|
||||
arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "await",
|
||||
},
|
||||
}),
|
||||
arena.alloc(task_await_apply_args),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
))
|
||||
} else {
|
||||
// The first Suffixed is in the middle of our Defs
|
||||
// We will keep the defs before the Suffixed in our Defs node
|
||||
// We take the defs after the Suffixed and create a new Defs node using the current loc_return
|
||||
// Then recurse on the new Defs node, wrap the result in an Apply(Task.await) and Closure,
|
||||
// which will become the new loc_return
|
||||
|
||||
let (before, after) = {
|
||||
let values = defs.split_values_either_side_of(tag_index);
|
||||
(values.before, values.after)
|
||||
};
|
||||
|
||||
// If there are no defs after, then just use loc_ret as we dont need a Defs node
|
||||
let defs_after_suffixed_desugared = {
|
||||
if !after.is_empty() {
|
||||
desugar_defs_node_suffixed(
|
||||
arena,
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Defs(arena.alloc(after), loc_ret),
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
loc_ret
|
||||
}
|
||||
};
|
||||
|
||||
// Unwrap Suffixed def within Apply, and the pattern so we can use in the call to Task.await
|
||||
let (suffixed_sub_apply_loc, pattern) = unwrap_suffixed_def_and_pattern(
|
||||
arena,
|
||||
loc_expr.region,
|
||||
defs.value_defs[value_index],
|
||||
);
|
||||
|
||||
// Create Closure for the result of the recursion,
|
||||
// use the pattern from our Suffixed Def as closure argument
|
||||
let closure_expr =
|
||||
Closure(arena.alloc([*pattern]), defs_after_suffixed_desugared);
|
||||
|
||||
// Apply arguments to Task.await, first is the unwrapped Suffix expr second is the Closure
|
||||
let mut task_await_apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
|
||||
|
||||
task_await_apply_args
|
||||
.push(arena.alloc(Loc::at(loc_expr.region, suffixed_sub_apply_loc)));
|
||||
task_await_apply_args
|
||||
.push(arena.alloc(Loc::at(loc_expr.region, closure_expr)));
|
||||
|
||||
let new_loc_return = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Apply(
|
||||
arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "await",
|
||||
},
|
||||
}),
|
||||
arena.alloc(task_await_apply_args),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
));
|
||||
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Defs(arena.alloc(before), new_loc_return),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
// `desugar_defs_node_values` is called recursively in `desugar_expr` and we
|
||||
// only we only want to unwrap suffixed nodes if they are a top level def.
|
||||
//
|
||||
// check here first so we only unwrap the expressions once, and after they have
|
||||
// been desugared
|
||||
if top_level_def {
|
||||
for value_def in defs.value_defs.iter_mut() {
|
||||
*value_def = desugar_value_def_suffixed(arena, *value_def);
|
||||
}
|
||||
_ => unreachable!(
|
||||
"should only be passed a Defs node as it is called from within desugar_expr for Defs"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap Suffixed def within Apply, and the pattern so we can use in the call to Task.await
|
||||
fn unwrap_suffixed_def_and_pattern<'a>(
|
||||
arena: &'a Bump,
|
||||
region: Region,
|
||||
value_def: ValueDef<'a>,
|
||||
) -> (
|
||||
roc_parse::ast::Expr<'a>,
|
||||
&'a Loc<roc_parse::ast::Pattern<'a>>,
|
||||
) {
|
||||
/// For each top-level ValueDef in our module, we will unwrap any suffixed
|
||||
/// expressions
|
||||
///
|
||||
/// e.g. `say! "hi"` desugars to `Task.await (say "hi") -> \{} -> ...`
|
||||
pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) -> ValueDef<'a> {
|
||||
use ValueDef::*;
|
||||
|
||||
match value_def {
|
||||
ValueDef::Body(pattern, suffixed_expression) => match suffixed_expression.value {
|
||||
// The Suffixed has arguments applied e.g. `Stdout.line! "Hello World"`
|
||||
Apply(sub_loc, suffixed_args, called_via) => match sub_loc.value {
|
||||
Suffixed(sub_expr) => (
|
||||
Apply(
|
||||
arena.alloc(Loc::at(region, *sub_expr)),
|
||||
suffixed_args,
|
||||
called_via,
|
||||
),
|
||||
pattern,
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
// note called_from_def is passed as `false` as this is a top_level_def
|
||||
match unwrap_suffixed_expression(arena, loc_expr, None) {
|
||||
Ok(new_expr) => Body(loc_pattern, new_expr),
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => {
|
||||
let ok_wrapped_return = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Apply(
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "ok",
|
||||
suffixed: 0,
|
||||
},
|
||||
)),
|
||||
arena.alloc([sub_new]),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
));
|
||||
Body(
|
||||
loc_pattern,
|
||||
apply_task_await(
|
||||
arena,
|
||||
loc_expr.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
ok_wrapped_return,
|
||||
),
|
||||
)
|
||||
}
|
||||
Err(..) => Body(
|
||||
loc_pattern,
|
||||
arena.alloc(Loc::at(loc_expr.region, MalformedSuffixed(loc_expr))),
|
||||
),
|
||||
_ => unreachable!("should have a suffixed Apply inside Body def"),
|
||||
},
|
||||
// The Suffixed has NIL arguments applied e.g. `Stdin.line!`
|
||||
Suffixed(sub_expr) => (*sub_expr, pattern),
|
||||
_ => {
|
||||
unreachable!("should have a suffixed Apply inside Body def")
|
||||
}
|
||||
},
|
||||
_ => unreachable!("should have a suffixed Body def"),
|
||||
}
|
||||
ann @ Annotation(_, _) => ann,
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment,
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => {
|
||||
// note called_from_def is passed as `false` as this is a top_level_def
|
||||
match unwrap_suffixed_expression(arena, body_expr, None) {
|
||||
Ok(new_expr) => AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment,
|
||||
body_pattern,
|
||||
body_expr: new_expr,
|
||||
},
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment,
|
||||
body_pattern,
|
||||
body_expr: apply_task_await(arena, body_expr.region, sub_arg, sub_pat, sub_new),
|
||||
},
|
||||
Err(..) => AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment,
|
||||
body_pattern,
|
||||
body_expr: arena.alloc(Loc::at(body_expr.region, MalformedSuffixed(body_expr))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TODO support desugaring of Dbg, Expect, and ExpectFx
|
||||
Dbg { .. } | Expect { .. } | ExpectFx { .. } => value_def,
|
||||
|
||||
Stmt(..) => {
|
||||
internal_error!(
|
||||
"this should have been desugared into a Body(..) before this call in desugar_expr"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,6 +290,7 @@ pub fn desugar_expr<'a>(
|
||||
| Underscore { .. }
|
||||
| MalformedIdent(_, _)
|
||||
| MalformedClosure
|
||||
| MalformedSuffixed(..)
|
||||
| PrecedenceConflict { .. }
|
||||
| MultipleRecordBuilders { .. }
|
||||
| UnappliedRecordBuilder { .. }
|
||||
@ -581,14 +492,10 @@ pub fn desugar_expr<'a>(
|
||||
),
|
||||
Defs(defs, loc_ret) => {
|
||||
let mut defs = (*defs).clone();
|
||||
desugar_defs_node_values(arena, &mut defs, src, line_info, module_path);
|
||||
desugar_defs_node_values(arena, &mut defs, src, line_info, module_path, false);
|
||||
let loc_ret = desugar_expr(arena, loc_ret, src, line_info, module_path);
|
||||
|
||||
// Desugar any Suffixed nodes
|
||||
desugar_defs_node_suffixed(
|
||||
arena,
|
||||
arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret))),
|
||||
)
|
||||
arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret)))
|
||||
}
|
||||
Apply(loc_fn, loc_args, called_via) => {
|
||||
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
@ -699,10 +606,12 @@ pub fn desugar_expr<'a>(
|
||||
Negate => Var {
|
||||
module_name: ModuleName::NUM,
|
||||
ident: "neg",
|
||||
suffixed: 0,
|
||||
},
|
||||
Not => Var {
|
||||
module_name: ModuleName::BOOL,
|
||||
ident: "not",
|
||||
suffixed: 0,
|
||||
},
|
||||
};
|
||||
let loc_fn_var = arena.alloc(Loc { region, value });
|
||||
@ -800,6 +709,7 @@ pub fn desugar_expr<'a>(
|
||||
let inspect_fn = Var {
|
||||
module_name: ModuleName::INSPECT,
|
||||
ident: "toStr",
|
||||
suffixed: 0,
|
||||
};
|
||||
let loc_inspect_fn_var = arena.alloc(Loc {
|
||||
value: inspect_fn,
|
||||
@ -839,31 +749,32 @@ pub fn desugar_expr<'a>(
|
||||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
LowLevelDbg(_, _, _) => unreachable!("Only exists after desugaring"),
|
||||
Suffixed(expr) => {
|
||||
// Rewrite `Suffixed(BinOps([args...], Var(...)))` to `BinOps([args...], Suffixed(Var(...)))`
|
||||
// This is to handle cases like e.g. `"Hello" |> line!`
|
||||
if let BinOps(args, sub_expr) = expr {
|
||||
return desugar_expr(
|
||||
arena,
|
||||
|
||||
// Replace an empty final def with a `Task.ok {}`
|
||||
EmptyDefsFinal => {
|
||||
let mut apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
|
||||
apply_args
|
||||
.push(arena.alloc(Loc::at(loc_expr.region, Expr::Record(Collection::empty()))));
|
||||
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Apply(
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
BinOps(
|
||||
args,
|
||||
arena.alloc(Loc::at(sub_expr.region, Suffixed(&sub_expr.value))),
|
||||
),
|
||||
Expr::Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "ok",
|
||||
suffixed: 0,
|
||||
},
|
||||
)),
|
||||
src,
|
||||
line_info,
|
||||
module_path,
|
||||
);
|
||||
}
|
||||
|
||||
// Suffixed are also desugared in Defs
|
||||
// Any nodes that don't get desugared will be caught by canonicalize_expr
|
||||
// and we can handle those cases as required
|
||||
loc_expr
|
||||
arena.alloc(apply_args),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// note this only exists after desugaring
|
||||
LowLevelDbg(_, _, _) => loc_expr,
|
||||
}
|
||||
}
|
||||
|
||||
@ -949,6 +860,7 @@ fn desugar_field<'a>(
|
||||
value: Var {
|
||||
module_name: "",
|
||||
ident: loc_str.value,
|
||||
suffixed: 0,
|
||||
},
|
||||
region: loc_str.region,
|
||||
};
|
||||
@ -1009,7 +921,7 @@ fn desugar_pattern<'a>(
|
||||
use roc_parse::ast::Pattern::*;
|
||||
|
||||
match pattern {
|
||||
Identifier(_)
|
||||
Identifier { .. }
|
||||
| Tag(_)
|
||||
| OpaqueRef(_)
|
||||
| NumLiteral(_)
|
||||
@ -1129,6 +1041,7 @@ fn record_builder_arg<'a>(
|
||||
value: Expr::Var {
|
||||
module_name: "",
|
||||
ident: arena.alloc("#".to_owned() + label.value),
|
||||
suffixed: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@ -1168,7 +1081,10 @@ fn record_builder_arg<'a>(
|
||||
|
||||
for label in apply_field_names.iter().rev() {
|
||||
let name = arena.alloc("#".to_owned() + label.value);
|
||||
let ident = roc_parse::ast::Pattern::Identifier(name);
|
||||
let ident = roc_parse::ast::Pattern::Identifier {
|
||||
ident: name,
|
||||
suffixed: 0,
|
||||
};
|
||||
|
||||
let arg_pattern = arena.alloc(Loc {
|
||||
value: ident,
|
||||
|
@ -623,6 +623,9 @@ pub fn canonicalize_expr<'a>(
|
||||
use Expr::*;
|
||||
|
||||
let (expr, output) = match expr {
|
||||
&ast::Expr::EmptyDefsFinal => {
|
||||
internal_error!("EmptyDefsFinal should have been desugared")
|
||||
}
|
||||
&ast::Expr::Num(str) => {
|
||||
let answer = num_expr_from_result(var_store, finish_parsing_num(str), region, env);
|
||||
|
||||
@ -1016,9 +1019,11 @@ pub fn canonicalize_expr<'a>(
|
||||
(expr, output)
|
||||
}
|
||||
}
|
||||
ast::Expr::Var { module_name, ident } => {
|
||||
canonicalize_var_lookup(env, var_store, scope, module_name, ident, region)
|
||||
}
|
||||
ast::Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: _, // TODO should we use suffixed here?
|
||||
} => canonicalize_var_lookup(env, var_store, scope, module_name, ident, region),
|
||||
ast::Expr::Underscore(name) => {
|
||||
// we parse underscores, but they are not valid expression syntax
|
||||
|
||||
@ -1375,6 +1380,10 @@ pub fn canonicalize_expr<'a>(
|
||||
|
||||
(RuntimeError(problem), Output::default())
|
||||
}
|
||||
ast::Expr::MalformedSuffixed(..) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
(RuntimeError(MalformedSuffixed(region)), Output::default())
|
||||
}
|
||||
ast::Expr::MultipleRecordBuilders(sub_expr) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
@ -1441,12 +1450,6 @@ pub fn canonicalize_expr<'a>(
|
||||
bad_expr
|
||||
);
|
||||
}
|
||||
bad_expr @ ast::Expr::Suffixed(_) => {
|
||||
internal_error!(
|
||||
"A suffixed expression did not get desugared somehow: {:#?}",
|
||||
bad_expr
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// At the end, diff used_idents and defined_idents to see which were unused.
|
||||
@ -2427,7 +2430,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
||||
| ast::Expr::IngestedFile(_, _)
|
||||
| ast::Expr::SpaceBefore(_, _)
|
||||
| ast::Expr::Str(StrLiteral::Block(_))
|
||||
| ast::Expr::SpaceAfter(_, _) => false,
|
||||
| ast::Expr::SpaceAfter(_, _)
|
||||
| ast::Expr::EmptyDefsFinal => false,
|
||||
// These can contain subexpressions, so we need to recursively check those
|
||||
ast::Expr::Str(StrLiteral::Line(segments)) => {
|
||||
segments.iter().all(|segment| match segment {
|
||||
@ -2453,6 +2457,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
||||
.iter()
|
||||
.all(|loc_field| is_valid_interpolation(&loc_field.value)),
|
||||
ast::Expr::MultipleRecordBuilders(loc_expr)
|
||||
| ast::Expr::MalformedSuffixed(loc_expr)
|
||||
| ast::Expr::UnappliedRecordBuilder(loc_expr)
|
||||
| ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. })
|
||||
| ast::Expr::UnaryOp(loc_expr, _)
|
||||
@ -2512,7 +2517,6 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
||||
ast::RecordBuilderField::SpaceBefore(_, _)
|
||||
| ast::RecordBuilderField::SpaceAfter(_, _) => false,
|
||||
}),
|
||||
ast::Expr::Suffixed(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ pub mod pattern;
|
||||
pub mod procedure;
|
||||
pub mod scope;
|
||||
pub mod string;
|
||||
pub mod suffixed;
|
||||
pub mod traverse;
|
||||
|
||||
pub use derive::DERIVED_REGION;
|
||||
|
@ -312,7 +312,8 @@ pub fn canonicalize_module_defs<'a>(
|
||||
// visited a BinOp node we'd recursively try to apply this to each of its nested
|
||||
// operators, and then again on *their* nested operators, ultimately applying the
|
||||
// rules multiple times unnecessarily.
|
||||
crate::desugar::desugar_defs_node_values(arena, loc_defs, src, &mut None, module_path);
|
||||
|
||||
crate::desugar::desugar_defs_node_values(arena, loc_defs, src, &mut None, module_path, true);
|
||||
|
||||
let mut rigid_variables = RigidVariables::default();
|
||||
|
||||
|
@ -265,7 +265,10 @@ pub fn canonicalize_def_header_pattern<'a>(
|
||||
|
||||
match pattern {
|
||||
// Identifiers that shadow ability members may appear (and may only appear) at the header of a def.
|
||||
Identifier(name) => {
|
||||
Identifier {
|
||||
ident: name,
|
||||
suffixed: _,
|
||||
} => {
|
||||
match scope.introduce_or_shadow_ability_member(
|
||||
pending_abilities_in_scope,
|
||||
(*name).into(),
|
||||
@ -373,12 +376,13 @@ pub fn canonicalize_pattern<'a>(
|
||||
use PatternType::*;
|
||||
|
||||
let can_pattern = match pattern {
|
||||
Identifier(name) => {
|
||||
match canonicalize_pattern_symbol(env, scope, output, region, permit_shadows, name) {
|
||||
Ok(symbol) => Pattern::Identifier(symbol),
|
||||
Err(pattern) => pattern,
|
||||
}
|
||||
}
|
||||
Identifier {
|
||||
ident: name,
|
||||
suffixed: _,
|
||||
} => match canonicalize_pattern_symbol(env, scope, output, region, permit_shadows, name) {
|
||||
Ok(symbol) => Pattern::Identifier(symbol),
|
||||
Err(pattern) => pattern,
|
||||
},
|
||||
Underscore(name) => {
|
||||
// An underscored identifier can't be used, but we'll still add it to the scope
|
||||
// for better error messages if someone tries to use it.
|
||||
@ -628,7 +632,10 @@ pub fn canonicalize_pattern<'a>(
|
||||
|
||||
for loc_pattern in patterns.iter() {
|
||||
match loc_pattern.value {
|
||||
Identifier(label) => {
|
||||
Identifier {
|
||||
ident: label,
|
||||
suffixed: _,
|
||||
} => {
|
||||
match scope.introduce(label.into(), region) {
|
||||
Ok(symbol) => {
|
||||
output.references.insert_bound(symbol);
|
||||
|
830
crates/compiler/can/src/suffixed.rs
Normal file
830
crates/compiler/can/src/suffixed.rs
Normal file
@ -0,0 +1,830 @@
|
||||
#![allow(clippy::manual_map)]
|
||||
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{is_loc_expr_suffixed, Pattern, ValueDef};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use std::cell::Cell;
|
||||
|
||||
thread_local! {
|
||||
// we use a thread_local here so that tests consistently give the same pattern
|
||||
static SUFFIXED_ANSWER_COUNTER: Cell<usize> = Cell::new(0);
|
||||
}
|
||||
|
||||
/// Provide an intermediate answer expression and pattern when unwrapping a
|
||||
/// (sub) expression
|
||||
///
|
||||
/// e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!a0 -> foo #!a0`
|
||||
fn next_suffixed_answer_pattern(arena: &Bump) -> (Expr, Pattern) {
|
||||
// Use the thread-local counter
|
||||
SUFFIXED_ANSWER_COUNTER.with(|counter| {
|
||||
let count = counter.get();
|
||||
counter.set(count + 1);
|
||||
|
||||
let answer_ident = arena.alloc(format!("#!a{}", count));
|
||||
|
||||
(
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: answer_ident,
|
||||
suffixed: 0,
|
||||
},
|
||||
Pattern::Identifier {
|
||||
ident: answer_ident.as_str(),
|
||||
suffixed: 0,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EUnwrapped<'a> {
|
||||
UnwrappedDefExpr(&'a Loc<Expr<'a>>),
|
||||
|
||||
UnwrappedSubExpr {
|
||||
/// the unwrapped expression argument for Task.await
|
||||
sub_arg: &'a Loc<Expr<'a>>,
|
||||
|
||||
/// the pattern for the closure
|
||||
sub_pat: &'a Loc<Pattern<'a>>,
|
||||
|
||||
/// the expression to replace the unwrapped
|
||||
sub_new: &'a Loc<Expr<'a>>,
|
||||
},
|
||||
|
||||
Malformed,
|
||||
}
|
||||
|
||||
fn init_unwrapped_err<'a>(
|
||||
arena: &'a Bump,
|
||||
unwrapped_expr: &'a Loc<Expr<'a>>,
|
||||
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
match maybe_def_pat {
|
||||
Some(..) => {
|
||||
// we have a def pattern, so no need to generate a new pattern
|
||||
// as this should only be created in the first call from a def
|
||||
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr))
|
||||
}
|
||||
None => {
|
||||
let (answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
|
||||
let sub_new = arena.alloc(Loc::at(unwrapped_expr.region, answer_var));
|
||||
let sub_pat = arena.alloc(Loc::at(unwrapped_expr.region, answer_pat));
|
||||
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg: unwrapped_expr,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Descend through the AST and unwrap each suffixed expression
|
||||
/// when an expression is unwrapped, we apply a `Task.await` and
|
||||
/// then descend through the AST again until there are no more suffixed
|
||||
/// expressions, or we hit an error
|
||||
pub fn unwrap_suffixed_expression<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
let unwrapped_expression = {
|
||||
match loc_expr.value {
|
||||
Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
} => {
|
||||
match suffixed {
|
||||
0 => Ok(loc_expr),
|
||||
1 => {
|
||||
let unwrapped_var = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: suffixed.saturating_sub(1),
|
||||
},
|
||||
));
|
||||
|
||||
init_unwrapped_err(arena, unwrapped_var, maybe_def_pat)
|
||||
}
|
||||
_ => {
|
||||
let unwrapped_var = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: 0,
|
||||
},
|
||||
));
|
||||
|
||||
// we generate an intermediate pattern `#!a0` etc
|
||||
// so we dont unwrap the definition pattern
|
||||
let (mut answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
|
||||
|
||||
// we transfer the suffix from the Var to the intermediate answer Var
|
||||
// as that will need to be unwrapped in a future call
|
||||
if let Expr::Var {
|
||||
module_name: "",
|
||||
ident: answer_ident,
|
||||
suffixed: 0,
|
||||
} = answer_var
|
||||
{
|
||||
answer_var = Expr::Var {
|
||||
module_name: "",
|
||||
ident: answer_ident,
|
||||
suffixed: suffixed.saturating_sub(1),
|
||||
}
|
||||
} else {
|
||||
internal_error!("expected a suffixed Var to be generated");
|
||||
}
|
||||
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg: unwrapped_var,
|
||||
sub_pat: arena.alloc(Loc::at(unwrapped_var.region, answer_pat)),
|
||||
sub_new: arena.alloc(Loc::at(unwrapped_var.region, answer_var)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, maybe_def_pat),
|
||||
|
||||
Expr::Apply(..) => {
|
||||
unwrap_suffixed_expression_apply_help(arena, loc_expr, maybe_def_pat)
|
||||
}
|
||||
|
||||
Expr::When(..) => unwrap_suffixed_expression_when_help(arena, loc_expr, maybe_def_pat),
|
||||
|
||||
Expr::If(..) => {
|
||||
unwrap_suffixed_expression_if_then_else_help(arena, loc_expr, maybe_def_pat)
|
||||
}
|
||||
|
||||
Expr::Closure(..) => {
|
||||
unwrap_suffixed_expression_closure_help(arena, loc_expr, maybe_def_pat)
|
||||
}
|
||||
|
||||
Expr::ParensAround(..) => {
|
||||
unwrap_suffixed_expression_parens_help(arena, loc_expr, maybe_def_pat)
|
||||
}
|
||||
|
||||
Expr::SpaceBefore(..) | Expr::SpaceAfter(..) => {
|
||||
internal_error!(
|
||||
"SpaceBefore and SpaceAfter should have been removed in desugar_expr"
|
||||
)
|
||||
}
|
||||
|
||||
Expr::BinOps(..) => {
|
||||
internal_error!("BinOps should have been desugared in desugar_expr")
|
||||
}
|
||||
|
||||
// we only need to unwrap some expressions, leave the rest as is
|
||||
_ => Ok(loc_expr),
|
||||
}
|
||||
};
|
||||
|
||||
// KEEP THIS HERE FOR DEBUGGING
|
||||
// USEFUL TO SEE THE UNWRAPPING
|
||||
// OF AST NODES AS THEY DESCEND
|
||||
// dbg!(&loc_expr, &unwrapped_expression);
|
||||
|
||||
unwrapped_expression
|
||||
}
|
||||
|
||||
pub fn unwrap_suffixed_expression_parens_help<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
_maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
match loc_expr.value {
|
||||
Expr::ParensAround(sub_loc_expr) => {
|
||||
// note we use `None` here as we always want to generate a new pattern from child expressions
|
||||
match unwrap_suffixed_expression(arena, arena.alloc(Loc::at_zero(*sub_loc_expr)), None)
|
||||
{
|
||||
Ok(new_expr) => {
|
||||
let new_parens = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
ParensAround(arena.alloc(new_expr.value)),
|
||||
));
|
||||
Ok(new_parens)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
|
||||
internal_error!("unreachable, child expressions from ParensAround should generate UnwrappedSubExpr instead");
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => {
|
||||
let new_parens = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
ParensAround(arena.alloc(sub_new.value)),
|
||||
));
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new: new_parens,
|
||||
})
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
_ => internal_error!("unreachable, expected a ParensAround node to be passed into unwrap_suffixed_expression_parens_help"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_suffixed_expression_closure_help<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
_maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
match loc_expr.value {
|
||||
Expr::Closure(closure_args, closure_loc_ret) => {
|
||||
|
||||
// Check to make sure that arguments are not suffixed
|
||||
let suffixed_arg_count = closure_args
|
||||
.iter()
|
||||
.filter(|loc_pat| loc_pat.value.is_suffixed())
|
||||
.count();
|
||||
|
||||
if suffixed_arg_count > 0 {
|
||||
debug_assert!(false,"closure arguments should not be suffixed");
|
||||
return Err(EUnwrapped::Malformed);
|
||||
}
|
||||
|
||||
// note we use `None` here as we don't want to pass a DefExpr up and
|
||||
// unwrap the definition pattern for the closure
|
||||
match unwrap_suffixed_expression(arena, closure_loc_ret, None) {
|
||||
Ok(unwrapped_expr) => {
|
||||
let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, unwrapped_expr)));
|
||||
Ok(new_closure)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_closure_loc_ret = apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new);
|
||||
let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret)));
|
||||
Ok(new_closure)
|
||||
}
|
||||
Err(err) => {
|
||||
debug_assert!(false,"the closure Defs was malformd, got {:#?}", err);
|
||||
Err(EUnwrapped::Malformed)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => internal_error!("unreachable, expected a Closure node to be passed into unwrap_suffixed_expression_closure_help"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_suffixed_expression_apply_help<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
match loc_expr.value {
|
||||
Expr::Apply(function, apply_args, called_via) => {
|
||||
|
||||
// Any suffixed arguments will be innermost, therefore we unwrap those first
|
||||
let local_args = arena.alloc_slice_copy(apply_args);
|
||||
for (_, arg) in local_args.iter_mut().enumerate() {
|
||||
match unwrap_suffixed_expression(arena, arg, maybe_def_pat) {
|
||||
Ok(new_arg) => {
|
||||
*arg = new_arg;
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_arg)) => {
|
||||
*arg = unwrapped_arg;
|
||||
|
||||
let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via)));
|
||||
|
||||
return Err(EUnwrapped::UnwrappedDefExpr(new_apply));
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg }) => {
|
||||
|
||||
*arg = new_arg;
|
||||
|
||||
let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via)));
|
||||
return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply});
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
// special case for when our Apply function is a suffixed Var (but not multiple suffixed)
|
||||
if let Expr::Var { module_name, ident, suffixed } = function.value {
|
||||
if suffixed == 1 {
|
||||
let unwrapped_function = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: suffixed - 1,
|
||||
},
|
||||
));
|
||||
|
||||
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via)));
|
||||
|
||||
return init_unwrapped_err(arena, new_apply, maybe_def_pat);
|
||||
}
|
||||
}
|
||||
|
||||
// function is another expression
|
||||
match unwrap_suffixed_expression(arena, function, maybe_def_pat) {
|
||||
Ok(new_function) => {
|
||||
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via)));
|
||||
Ok(new_apply)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_function)) => {
|
||||
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via)));
|
||||
Err(EUnwrapped::UnwrappedDefExpr(new_apply))
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new }) => {
|
||||
|
||||
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via)));
|
||||
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply})
|
||||
}
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
_ => internal_error!("unreachable, expected an Apply node to be passed into unwrap_suffixed_expression_apply_help"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap if-then-else statements
|
||||
pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
match loc_expr.value {
|
||||
Expr::If(if_thens, final_else_branch) => {
|
||||
for (index, if_then) in if_thens.iter().enumerate() {
|
||||
let (current_if_then_statement, current_if_then_expression) = if_then;
|
||||
|
||||
// unwrap suffixed (innermost) expressions e.g. `if true then doThing! then ...`
|
||||
if is_loc_expr_suffixed(current_if_then_expression) {
|
||||
// split if_thens around the current index
|
||||
let (before, after) = roc_parse::ast::split_around(if_thens, index);
|
||||
|
||||
match unwrap_suffixed_expression(arena, current_if_then_expression, None) {
|
||||
Ok(unwrapped_expression) => {
|
||||
let mut new_if_thens = Vec::new_in(arena);
|
||||
|
||||
new_if_thens.extend(before);
|
||||
new_if_thens.push((*current_if_then_statement, *unwrapped_expression));
|
||||
new_if_thens.extend(after);
|
||||
|
||||
let new_if = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::If(
|
||||
arena.alloc_slice_copy(new_if_thens.as_slice()),
|
||||
final_else_branch,
|
||||
),
|
||||
));
|
||||
|
||||
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
|
||||
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => {
|
||||
let ok_wrapped_return = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Apply(
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "ok",
|
||||
suffixed: 0,
|
||||
},
|
||||
)),
|
||||
arena.alloc([sub_new]),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
));
|
||||
|
||||
let unwrapped_expression = apply_task_await(
|
||||
arena,
|
||||
sub_arg.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
ok_wrapped_return,
|
||||
);
|
||||
|
||||
let mut new_if_thens = Vec::new_in(arena);
|
||||
|
||||
new_if_thens.extend(before);
|
||||
new_if_thens.push((*current_if_then_statement, *unwrapped_expression));
|
||||
new_if_thens.extend(after);
|
||||
|
||||
let new_if = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::If(
|
||||
arena.alloc_slice_copy(new_if_thens.as_slice()),
|
||||
final_else_branch,
|
||||
),
|
||||
));
|
||||
|
||||
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
|
||||
}
|
||||
Err(EUnwrapped::Malformed) => return Err(EUnwrapped::Malformed),
|
||||
}
|
||||
}
|
||||
|
||||
// unwrap suffixed statements e.g. `if isThing! then ...`
|
||||
// note we want to split and nest if-then's so we only run Task's
|
||||
// that are required
|
||||
if is_loc_expr_suffixed(current_if_then_statement) {
|
||||
// split if_thens around the current index
|
||||
let (before, after) = roc_parse::ast::split_around(if_thens, index);
|
||||
|
||||
match unwrap_suffixed_expression(arena, current_if_then_statement, None) {
|
||||
Ok(unwrapped_statement) => {
|
||||
let mut new_if_thens = Vec::new_in(arena);
|
||||
|
||||
new_if_thens.push((*unwrapped_statement, *current_if_then_expression));
|
||||
new_if_thens.extend(after);
|
||||
|
||||
let new_if = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::If(
|
||||
arena.alloc_slice_copy(new_if_thens.as_slice()),
|
||||
final_else_branch,
|
||||
),
|
||||
));
|
||||
|
||||
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
|
||||
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => {
|
||||
if before.is_empty() {
|
||||
let mut new_if_thens = Vec::new_in(arena);
|
||||
|
||||
new_if_thens.extend(before);
|
||||
new_if_thens.push((*sub_new, *current_if_then_expression));
|
||||
new_if_thens.extend(after);
|
||||
|
||||
let new_if = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::If(
|
||||
arena.alloc_slice_copy(new_if_thens.as_slice()),
|
||||
final_else_branch,
|
||||
),
|
||||
));
|
||||
|
||||
let unwrapped_if_then = apply_task_await(
|
||||
arena,
|
||||
sub_arg.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
new_if,
|
||||
);
|
||||
|
||||
return unwrap_suffixed_expression(
|
||||
arena,
|
||||
unwrapped_if_then,
|
||||
maybe_def_pat,
|
||||
);
|
||||
} else {
|
||||
let mut after_if_thens = Vec::new_in(arena);
|
||||
|
||||
after_if_thens.push((*sub_new, *current_if_then_expression));
|
||||
after_if_thens.extend(after);
|
||||
|
||||
let after_if = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::If(
|
||||
arena.alloc_slice_copy(after_if_thens.as_slice()),
|
||||
final_else_branch,
|
||||
),
|
||||
));
|
||||
|
||||
let after_if_then = apply_task_await(
|
||||
arena,
|
||||
sub_arg.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
after_if,
|
||||
);
|
||||
|
||||
let before_if_then = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::If(before, after_if_then),
|
||||
));
|
||||
|
||||
return unwrap_suffixed_expression(
|
||||
arena,
|
||||
before_if_then,
|
||||
maybe_def_pat,
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(EUnwrapped::Malformed) => return Err(EUnwrapped::Malformed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check the final_else_branch
|
||||
match unwrap_suffixed_expression(arena, final_else_branch, None) {
|
||||
Ok(unwrapped_final_else) => {
|
||||
return Ok(arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::If(if_thens, unwrapped_final_else),
|
||||
)));
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
|
||||
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => {
|
||||
let ok_wrapped_return = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Apply(
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "ok",
|
||||
suffixed: 0,
|
||||
},
|
||||
)),
|
||||
arena.alloc([sub_new]),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
));
|
||||
|
||||
let unwrapped_final_else = apply_task_await(
|
||||
arena,
|
||||
sub_arg.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
ok_wrapped_return,
|
||||
);
|
||||
|
||||
let new_if = arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::If(if_thens, unwrapped_final_else),
|
||||
));
|
||||
|
||||
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
|
||||
}
|
||||
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
|
||||
}
|
||||
}
|
||||
_ => internal_error!("unreachable, expected an If expression to desugar"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_suffixed_expression_when_help<'a>(
|
||||
_arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
_maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
Ok(loc_expr)
|
||||
}
|
||||
|
||||
pub fn unwrap_suffixed_expression_defs_help<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
|
||||
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
|
||||
match loc_expr.value {
|
||||
Expr::Defs(defs, loc_ret) => {
|
||||
|
||||
let mut local_defs = defs.clone();
|
||||
let tags = local_defs.tags.clone();
|
||||
|
||||
// try an unwrap each def, if none can be unwrapped, then try to unwrap the loc_ret
|
||||
for (tag_index, type_or_value_def_index) in tags.iter().enumerate() {
|
||||
use ValueDef::*;
|
||||
|
||||
let mut current_value_def = match type_or_value_def_index.split() {
|
||||
Ok(..) => {
|
||||
// ignore type definitions
|
||||
continue;
|
||||
},
|
||||
Err(value_index) => *local_defs.value_defs.get(value_index.index()).unwrap(),
|
||||
};
|
||||
|
||||
let maybe_suffixed_value_def = match current_value_def {
|
||||
Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) => None,
|
||||
AnnotatedBody { body_pattern, body_expr, .. } => Some((body_pattern, body_expr)),
|
||||
Body (def_pattern, def_expr, .. ) => Some((def_pattern, def_expr)),
|
||||
};
|
||||
|
||||
match maybe_suffixed_value_def {
|
||||
None => {
|
||||
// We can't unwrap this def type, continue
|
||||
},
|
||||
Some((def_pattern, def_expr)) => {
|
||||
match unwrap_suffixed_expression(arena, def_expr, Some(def_pattern)) {
|
||||
Ok(unwrapped_def) => {
|
||||
current_value_def.replace_expr(unwrapped_def);
|
||||
local_defs.replace_with_value_def(tag_index, current_value_def, def_expr.region);
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => {
|
||||
let split_defs = local_defs.split_defs_around(tag_index);
|
||||
let before_empty = split_defs.before.is_empty();
|
||||
let after_empty = split_defs.after.is_empty();
|
||||
if before_empty && after_empty {
|
||||
// NIL before, NIL after -> SINGLE DEF
|
||||
let next_expr = match unwrap_suffixed_expression(arena,loc_ret,maybe_def_pat) {
|
||||
Ok(next_expr) => next_expr,
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => {
|
||||
// TODO handle case when we have maybe_def_pat so can return an unwrapped up
|
||||
return Err(EUnwrapped::Malformed);
|
||||
},
|
||||
};
|
||||
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr), maybe_def_pat);
|
||||
} else if before_empty {
|
||||
// NIL before, SOME after -> FIRST DEF
|
||||
let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret)));
|
||||
|
||||
let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){
|
||||
Ok(next_expr) => next_expr,
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => {
|
||||
// TODO handle case when we have maybe_def_pat so can return an unwrapped up
|
||||
return Err(EUnwrapped::Malformed);
|
||||
},
|
||||
};
|
||||
|
||||
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr), maybe_def_pat);
|
||||
} else if after_empty {
|
||||
// SOME before, NIL after -> LAST DEF
|
||||
match unwrap_suffixed_expression(arena,loc_ret,maybe_def_pat){
|
||||
Ok(new_loc_ret) => {
|
||||
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await)));
|
||||
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
|
||||
},
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new);
|
||||
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await)));
|
||||
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
|
||||
// TODO confirm this is correct with test case
|
||||
return Err(EUnwrapped::Malformed);
|
||||
}
|
||||
Err(EUnwrapped::Malformed) => {
|
||||
return Err(EUnwrapped::Malformed);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// SOME before, SOME after -> MIDDLE DEF
|
||||
let after_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret)));
|
||||
|
||||
match unwrap_suffixed_expression(arena,after_defs,maybe_def_pat){
|
||||
Ok(new_loc_ret) => {
|
||||
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await)));
|
||||
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
|
||||
},
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new);
|
||||
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await)));
|
||||
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => {
|
||||
// TODO handle case when we have maybe_def_pat so can return an unwrapped up
|
||||
return Err(EUnwrapped::Malformed);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_body_def = ValueDef::Body(def_pattern, sub_new);
|
||||
local_defs.replace_with_value_def(tag_index,new_body_def, sub_new.region);
|
||||
let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret)));
|
||||
let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr);
|
||||
return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat);
|
||||
}
|
||||
Err(err) => return Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to unwrap the loc_ret
|
||||
match unwrap_suffixed_expression(arena,loc_ret,maybe_def_pat){
|
||||
Ok(new_loc_ret) => {
|
||||
Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))))
|
||||
},
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new);
|
||||
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)));
|
||||
unwrap_suffixed_expression(arena, new_defs, maybe_def_pat)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..)) => {
|
||||
// TODO confirm this is correct with test case
|
||||
Err(EUnwrapped::Malformed)
|
||||
}
|
||||
Err(EUnwrapped::Malformed) => {
|
||||
Err(EUnwrapped::Malformed)
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => internal_error!("unreachable, expected a Defs node to be passed into unwrap_suffixed_expression_defs_help"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for `Task.await (loc_arg) \loc_pat -> loc_new`
|
||||
pub fn apply_task_await<'a>(
|
||||
arena: &'a Bump,
|
||||
region: Region,
|
||||
loc_arg: &'a Loc<Expr<'a>>,
|
||||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_new: &'a Loc<Expr<'a>>,
|
||||
) -> &'a Loc<Expr<'a>> {
|
||||
// If the pattern and the new are the same then we don't need to unwrap anything
|
||||
// e.g. `Task.await foo \{} -> Task.ok {}` is the same as `foo`
|
||||
if is_pattern_empty_record(loc_pat) && is_expr_task_ok(loc_new) {
|
||||
return loc_arg;
|
||||
}
|
||||
|
||||
let mut task_await_apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
|
||||
|
||||
// apply the unwrapped suffixed expression
|
||||
task_await_apply_args.push(loc_arg);
|
||||
|
||||
// apply the closure
|
||||
let mut closure_pattern = Vec::new_in(arena);
|
||||
closure_pattern.push(*loc_pat);
|
||||
task_await_apply_args.push(arena.alloc(Loc::at(
|
||||
region,
|
||||
Closure(arena.alloc_slice_copy(closure_pattern.as_slice()), loc_new),
|
||||
)));
|
||||
|
||||
arena.alloc(Loc::at(
|
||||
region,
|
||||
Apply(
|
||||
arena.alloc(Loc {
|
||||
region,
|
||||
value: Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "await",
|
||||
suffixed: 0,
|
||||
},
|
||||
}),
|
||||
arena.alloc(task_await_apply_args),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn is_pattern_empty_record<'a>(loc_pat: &'a Loc<Pattern<'a>>) -> bool {
|
||||
match loc_pat.value {
|
||||
Pattern::RecordDestructure(collection) => collection.is_empty(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_expr_task_ok<'a>(loc_expr: &'a Loc<Expr<'a>>) -> bool {
|
||||
match loc_expr.value {
|
||||
Expr::Apply(function, arguments, _) => {
|
||||
let is_task_ok = match function.value {
|
||||
Var {
|
||||
module_name, ident, ..
|
||||
} => module_name == ModuleName::TASK && ident == "ok",
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let is_arg_empty_record = arguments
|
||||
.first()
|
||||
.map(|arg_loc_expr| match arg_loc_expr.value {
|
||||
Expr::Record(collection) => collection.is_empty(),
|
||||
_ => false,
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
is_task_ok && is_arg_empty_record
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
641
crates/compiler/can/tests/test_suffixed.rs
Normal file
641
crates/compiler/can/tests/test_suffixed.rs
Normal file
File diff suppressed because one or more lines are too long
@ -12,5 +12,6 @@ roc_collections = { path = "../collections" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_parse = { path = "../parse" }
|
||||
roc_region = { path = "../region" }
|
||||
roc_error_macros = { path = "../../error_macros" }
|
||||
|
||||
bumpalo.workspace = true
|
||||
|
@ -196,10 +196,11 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||
Expect { condition, .. } => condition.is_multiline(),
|
||||
ExpectFx { condition, .. } => condition.is_multiline(),
|
||||
Dbg { condition, .. } => condition.is_multiline(),
|
||||
Stmt(loc_expr) => loc_expr.is_multiline(),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_with_options(&self, buf: &mut Buf, _parens: Parens, newlines: Newlines, indent: u16) {
|
||||
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
||||
use roc_parse::ast::ValueDef::*;
|
||||
match self {
|
||||
Annotation(loc_pattern, loc_annotation) => {
|
||||
@ -238,6 +239,7 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||
buf.newline();
|
||||
fmt_body(buf, &body_pattern.value, &body_expr.value, indent);
|
||||
}
|
||||
Stmt(loc_expr) => loc_expr.format_with_options(buf, parens, newlines, indent),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -357,9 +359,19 @@ pub fn fmt_defs(buf: &mut Buf, defs: &Defs, indent: u16) {
|
||||
}
|
||||
|
||||
pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>, indent: u16) {
|
||||
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||
buf.indent(indent);
|
||||
buf.push_str(" =");
|
||||
// Check if this is an assignment into the unit value
|
||||
let is_unit_assignment = if let Pattern::RecordDestructure(collection) = pattern {
|
||||
collection.is_empty()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// Don't format the `{} =` for defs with this pattern
|
||||
if !is_unit_assignment {
|
||||
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||
buf.indent(indent);
|
||||
buf.push_str(" =");
|
||||
}
|
||||
|
||||
if body.is_multiline() {
|
||||
match body {
|
||||
|
@ -9,8 +9,8 @@ use crate::spaces::{
|
||||
use crate::Buf;
|
||||
use roc_module::called_via::{self, BinOp};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, Pattern,
|
||||
RecordBuilderField, WhenBranch,
|
||||
is_loc_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces,
|
||||
Pattern, RecordBuilderField, WhenBranch,
|
||||
};
|
||||
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||
use roc_parse::ident::Accessor;
|
||||
@ -31,6 +31,8 @@ impl<'a> Formattable for Expr<'a> {
|
||||
true
|
||||
}
|
||||
|
||||
MalformedSuffixed(loc_expr) => loc_expr.is_multiline(),
|
||||
|
||||
// These expressions never have newlines
|
||||
Float(..)
|
||||
| Num(..)
|
||||
@ -46,6 +48,7 @@ impl<'a> Formattable for Expr<'a> {
|
||||
| Tag(_)
|
||||
| OpaqueRef(_)
|
||||
| IngestedFile(_, _)
|
||||
| EmptyDefsFinal
|
||||
| Crash => false,
|
||||
|
||||
// These expressions always have newlines
|
||||
@ -107,7 +110,6 @@ impl<'a> Formattable for Expr<'a> {
|
||||
Tuple(fields) => is_collection_multiline(fields),
|
||||
RecordUpdate { fields, .. } => is_collection_multiline(fields),
|
||||
RecordBuilder(fields) => is_collection_multiline(fields),
|
||||
Suffixed(subexpr) => subexpr.is_multiline(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,7 +170,11 @@ impl<'a> Formattable for Expr<'a> {
|
||||
Str(literal) => {
|
||||
fmt_str_literal(buf, *literal, indent);
|
||||
}
|
||||
Var { module_name, ident } => {
|
||||
Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
if !module_name.is_empty() {
|
||||
buf.push_str(module_name);
|
||||
@ -176,6 +182,11 @@ impl<'a> Formattable for Expr<'a> {
|
||||
}
|
||||
|
||||
buf.push_str(ident);
|
||||
|
||||
let count: u8 = *suffixed;
|
||||
for _ in 0..count {
|
||||
buf.push('!');
|
||||
}
|
||||
}
|
||||
Underscore(name) => {
|
||||
buf.indent(indent);
|
||||
@ -417,6 +428,9 @@ impl<'a> Formattable for Expr<'a> {
|
||||
indent,
|
||||
);
|
||||
}
|
||||
EmptyDefsFinal => {
|
||||
// no need to print anything
|
||||
}
|
||||
_ => {
|
||||
buf.ensure_ends_with_newline();
|
||||
buf.indent(indent);
|
||||
@ -433,6 +447,9 @@ impl<'a> Formattable for Expr<'a> {
|
||||
buf.push(')');
|
||||
}
|
||||
}
|
||||
EmptyDefsFinal => {
|
||||
// no need to print anything
|
||||
}
|
||||
Expect(condition, continuation) => {
|
||||
fmt_expect(buf, condition, continuation, self.is_multiline(), indent);
|
||||
}
|
||||
@ -495,28 +512,74 @@ impl<'a> Formattable for Expr<'a> {
|
||||
}
|
||||
}
|
||||
RecordAccess(expr, key) => {
|
||||
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
|
||||
// Check for any `!` suffixes and format these at the end of expression
|
||||
let (expr_to_format, suffix_count) = if let Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
} = expr
|
||||
{
|
||||
(
|
||||
Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: 0,
|
||||
},
|
||||
suffixed,
|
||||
)
|
||||
} else {
|
||||
(**expr, &0u8)
|
||||
};
|
||||
|
||||
expr_to_format.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
|
||||
buf.push('.');
|
||||
buf.push_str(key);
|
||||
|
||||
for _ in 0..*suffix_count {
|
||||
buf.push('!');
|
||||
}
|
||||
}
|
||||
TupleAccess(expr, key) => {
|
||||
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
|
||||
// Check for any `!` suffixes and format these at the end of expression
|
||||
let (expr_to_format, suffix_count) = if let Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
} = expr
|
||||
{
|
||||
(
|
||||
Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: 0,
|
||||
},
|
||||
suffixed,
|
||||
)
|
||||
} else {
|
||||
(**expr, &0u8)
|
||||
};
|
||||
|
||||
expr_to_format.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
|
||||
buf.push('.');
|
||||
buf.push_str(key);
|
||||
|
||||
for _ in 0..*suffix_count {
|
||||
buf.push('!');
|
||||
}
|
||||
}
|
||||
MalformedIdent(str, _) => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(str)
|
||||
}
|
||||
MalformedSuffixed(loc_expr) => {
|
||||
buf.indent(indent);
|
||||
loc_expr.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
MalformedClosure => {}
|
||||
PrecedenceConflict { .. } => {}
|
||||
MultipleRecordBuilders { .. } => {}
|
||||
UnappliedRecordBuilder { .. } => {}
|
||||
IngestedFile(_, _) => {}
|
||||
Suffixed(sub_expr) => {
|
||||
sub_expr.format_with_options(buf, parens, newlines, indent);
|
||||
buf.push('!');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -726,14 +789,32 @@ fn fmt_binops<'a>(
|
||||
|| loc_right_side.value.is_multiline()
|
||||
|| lefts.iter().any(|(expr, _)| expr.value.is_multiline());
|
||||
|
||||
let is_any_lefts_suffixed = lefts.iter().any(|(left, _)| is_loc_expr_suffixed(left));
|
||||
let is_right_suffixed = is_loc_expr_suffixed(loc_right_side);
|
||||
let is_any_suffixed = is_any_lefts_suffixed || is_right_suffixed;
|
||||
|
||||
let mut is_first = false;
|
||||
let mut adjusted_indent = indent;
|
||||
|
||||
if is_any_suffixed {
|
||||
// we only want to indent the remaining lines if this is a suffixed expression.
|
||||
is_first = true;
|
||||
}
|
||||
|
||||
for (loc_left_side, loc_binop) in lefts {
|
||||
let binop = loc_binop.value;
|
||||
|
||||
loc_left_side.format_with_options(buf, Parens::InOperator, Newlines::No, indent);
|
||||
loc_left_side.format_with_options(buf, Parens::InOperator, Newlines::No, adjusted_indent);
|
||||
|
||||
if is_first {
|
||||
// indent the remaining lines, but only if the expression is suffixed.
|
||||
is_first = false;
|
||||
adjusted_indent = indent + 4;
|
||||
}
|
||||
|
||||
if is_multiline {
|
||||
buf.ensure_ends_with_newline();
|
||||
buf.indent(indent);
|
||||
buf.indent(adjusted_indent);
|
||||
} else {
|
||||
buf.spaces(1);
|
||||
}
|
||||
@ -743,7 +824,7 @@ fn fmt_binops<'a>(
|
||||
buf.spaces(1);
|
||||
}
|
||||
|
||||
loc_right_side.format_with_options(buf, Parens::InOperator, Newlines::Yes, indent);
|
||||
loc_right_side.format_with_options(buf, Parens::InOperator, Newlines::Yes, adjusted_indent);
|
||||
}
|
||||
|
||||
fn format_spaces(buf: &mut Buf, spaces: &[CommentOrNewline], newlines: Newlines, indent: u16) {
|
||||
|
@ -64,7 +64,7 @@ impl<'a> Formattable for Pattern<'a> {
|
||||
}
|
||||
},
|
||||
|
||||
Pattern::Identifier(_)
|
||||
Pattern::Identifier { .. }
|
||||
| Pattern::Tag(_)
|
||||
| Pattern::OpaqueRef(_)
|
||||
| Pattern::Apply(_, _)
|
||||
@ -88,9 +88,16 @@ impl<'a> Formattable for Pattern<'a> {
|
||||
use self::Pattern::*;
|
||||
|
||||
match self {
|
||||
Identifier(string) => {
|
||||
Identifier {
|
||||
ident: string,
|
||||
suffixed,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(string)
|
||||
buf.push_str(string);
|
||||
|
||||
for _ in 0..*suffixed {
|
||||
buf.push('!');
|
||||
}
|
||||
}
|
||||
Tag(name) | OpaqueRef(name) => {
|
||||
buf.indent(indent);
|
||||
@ -270,13 +277,21 @@ impl<'a> Formattable for Pattern<'a> {
|
||||
buf.indent(indent);
|
||||
buf.push_str(string);
|
||||
}
|
||||
QualifiedIdentifier { module_name, ident } => {
|
||||
QualifiedIdentifier {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
if !module_name.is_empty() {
|
||||
buf.push_str(module_name);
|
||||
buf.push('.');
|
||||
}
|
||||
|
||||
for _ in 0..*suffixed {
|
||||
buf.push('!');
|
||||
}
|
||||
|
||||
buf.push_str(ident);
|
||||
}
|
||||
}
|
||||
|
@ -567,6 +567,7 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
||||
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||
preceding_comment: Region::zero(),
|
||||
},
|
||||
Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -668,6 +669,7 @@ impl<'a> RemoveSpaces<'a> for StrSegment<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Expr::EmptyDefsFinal => Expr::EmptyDefsFinal,
|
||||
Expr::Float(a) => Expr::Float(a),
|
||||
Expr::Num(a) => Expr::Num(a),
|
||||
Expr::NonBase10Int {
|
||||
@ -692,7 +694,15 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
||||
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
|
||||
Expr::RecordBuilder(a) => Expr::RecordBuilder(a.remove_spaces(arena)),
|
||||
Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)),
|
||||
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
||||
Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
} => Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
},
|
||||
Expr::Underscore(a) => Expr::Underscore(a),
|
||||
Expr::Tag(a) => Expr::Tag(a),
|
||||
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
|
||||
@ -755,13 +765,13 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
||||
}
|
||||
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
||||
Expr::MalformedClosure => Expr::MalformedClosure,
|
||||
Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a),
|
||||
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
|
||||
Expr::MultipleRecordBuilders(a) => Expr::MultipleRecordBuilders(a),
|
||||
Expr::UnappliedRecordBuilder(a) => Expr::UnappliedRecordBuilder(a),
|
||||
Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
Expr::SingleQuote(a) => Expr::Num(a),
|
||||
Expr::Suffixed(a) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -792,7 +802,7 @@ fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent {
|
||||
impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Pattern::Identifier(a) => Pattern::Identifier(a),
|
||||
Pattern::Identifier { ident, suffixed } => Pattern::Identifier { ident, suffixed },
|
||||
Pattern::Tag(a) => Pattern::Tag(a),
|
||||
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
|
||||
Pattern::Apply(a, b) => Pattern::Apply(
|
||||
@ -825,9 +835,15 @@ impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
||||
Pattern::Underscore(a) => Pattern::Underscore(a),
|
||||
Pattern::Malformed(a) => Pattern::Malformed(a),
|
||||
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
||||
Pattern::QualifiedIdentifier { module_name, ident } => {
|
||||
Pattern::QualifiedIdentifier { module_name, ident }
|
||||
}
|
||||
Pattern::QualifiedIdentifier {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
} => Pattern::QualifiedIdentifier {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
},
|
||||
Pattern::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Pattern::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
Pattern::SingleQuote(a) => Pattern::SingleQuote(a),
|
||||
|
@ -4768,33 +4768,38 @@ mod test_reporting {
|
||||
"
|
||||
);
|
||||
|
||||
test_report!(
|
||||
def_missing_final_expression,
|
||||
indoc!(
|
||||
r"
|
||||
f : Foo.foo
|
||||
"
|
||||
),
|
||||
@r#"
|
||||
── MISSING FINAL EXPRESSION in tmp/def_missing_final_expression/Test.roc ───────
|
||||
// TODO investigate this test. It was disabled in https://github.com/roc-lang/roc/pull/6634
|
||||
// as the way Defs without final expressions are handled. The changes probably shouldn't have
|
||||
// changed this error report. The exact same test_syntax test for this has not changed, so
|
||||
// we know the parser is parsing thesame thing. Therefore the way the AST is desugared must be
|
||||
// the cause of the change in error report.
|
||||
// test_report!(
|
||||
// def_missing_final_expression,
|
||||
// indoc!(
|
||||
// r"
|
||||
// f : Foo.foo
|
||||
// "
|
||||
// ),
|
||||
// @r#"
|
||||
// ── MISSING FINAL EXPRESSION in tmp/def_missing_final_expression/Test.roc ───────
|
||||
|
||||
I am partway through parsing a definition, but I got stuck here:
|
||||
// I am partway through parsing a definition, but I got stuck here:
|
||||
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ f : Foo.foo
|
||||
^
|
||||
// 1│ app "test" provides [main] to "./platform"
|
||||
// 2│
|
||||
// 3│ main =
|
||||
// 4│ f : Foo.foo
|
||||
// ^
|
||||
|
||||
This definition is missing a final expression. A nested definition
|
||||
must be followed by either another definition, or an expression
|
||||
// This definition is missing a final expression. A nested definition
|
||||
// must be followed by either another definition, or an expression
|
||||
|
||||
x = 4
|
||||
y = 2
|
||||
// x = 4
|
||||
// y = 2
|
||||
|
||||
x + y
|
||||
"#
|
||||
);
|
||||
// x + y
|
||||
// "#
|
||||
// );
|
||||
|
||||
test_report!(
|
||||
expression_indentation_end,
|
||||
|
@ -212,7 +212,11 @@ fn generate_entry_docs(
|
||||
match either_index.split() {
|
||||
Err(value_index) => match &defs.value_defs[value_index.index()] {
|
||||
ValueDef::Annotation(loc_pattern, loc_ann) => {
|
||||
if let Pattern::Identifier(identifier) = loc_pattern.value {
|
||||
if let Pattern::Identifier {
|
||||
ident: identifier,
|
||||
suffixed: _,
|
||||
} = loc_pattern.value
|
||||
{
|
||||
// Check if this module exposes the def
|
||||
if let Some(ident_id) = ident_ids.get_id(identifier) {
|
||||
let name = identifier.to_string();
|
||||
@ -233,7 +237,11 @@ fn generate_entry_docs(
|
||||
ann_type,
|
||||
..
|
||||
} => {
|
||||
if let Pattern::Identifier(identifier) = ann_pattern.value {
|
||||
if let Pattern::Identifier {
|
||||
ident: identifier,
|
||||
suffixed: _,
|
||||
} = ann_pattern.value
|
||||
{
|
||||
// Check if this module exposes the def
|
||||
if let Some(ident_id) = ident_ids.get_id(identifier) {
|
||||
let doc_def = DocDef {
|
||||
@ -249,7 +257,11 @@ fn generate_entry_docs(
|
||||
}
|
||||
|
||||
ValueDef::Body(pattern, _) => {
|
||||
if let Pattern::Identifier(identifier) = pattern.value {
|
||||
if let Pattern::Identifier {
|
||||
ident: identifier,
|
||||
suffixed: _,
|
||||
} = pattern.value
|
||||
{
|
||||
// Check if this module exposes the def
|
||||
if let Some(ident_id) = ident_ids.get_id(identifier) {
|
||||
let doc_def = DocDef {
|
||||
@ -275,6 +287,25 @@ fn generate_entry_docs(
|
||||
ValueDef::ExpectFx { .. } => {
|
||||
// Don't generate docs for `expect-fx`s
|
||||
}
|
||||
|
||||
ValueDef::Stmt(loc_expr) => {
|
||||
if let roc_parse::ast::Expr::Var {
|
||||
ident: identifier, ..
|
||||
} = loc_expr.value
|
||||
{
|
||||
// Check if this module exposes the def
|
||||
if let Some(ident_id) = ident_ids.get_id(identifier) {
|
||||
let doc_def = DocDef {
|
||||
name: identifier.to_string(),
|
||||
type_annotation: TypeAnnotation::NoTypeAnn,
|
||||
type_vars: Vec::new(),
|
||||
symbol: Symbol::new(home, ident_id),
|
||||
docs,
|
||||
};
|
||||
doc_entries.push(DocEntry::DocDef(doc_def));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Ok(type_index) => match &defs.type_defs[type_index.index()] {
|
||||
@ -285,7 +316,11 @@ fn generate_entry_docs(
|
||||
let mut type_vars = Vec::new();
|
||||
|
||||
for var in vars.iter() {
|
||||
if let Pattern::Identifier(ident_name) = var.value {
|
||||
if let Pattern::Identifier {
|
||||
ident: ident_name,
|
||||
suffixed: _,
|
||||
} = var.value
|
||||
{
|
||||
type_vars.push(ident_name.to_string());
|
||||
}
|
||||
}
|
||||
@ -319,7 +354,11 @@ fn generate_entry_docs(
|
||||
let mut type_vars = Vec::new();
|
||||
|
||||
for var in vars.iter() {
|
||||
if let Pattern::Identifier(ident_name) = var.value {
|
||||
if let Pattern::Identifier {
|
||||
ident: ident_name,
|
||||
suffixed: _,
|
||||
} = var.value
|
||||
{
|
||||
type_vars.push(ident_name.to_string());
|
||||
}
|
||||
}
|
||||
@ -343,7 +382,11 @@ fn generate_entry_docs(
|
||||
let mut type_vars = Vec::new();
|
||||
|
||||
for var in vars.iter() {
|
||||
if let Pattern::Identifier(ident_name) = var.value {
|
||||
if let Pattern::Identifier {
|
||||
ident: ident_name,
|
||||
suffixed: _,
|
||||
} = var.value
|
||||
{
|
||||
type_vars.push(ident_name.to_string());
|
||||
}
|
||||
}
|
||||
@ -605,7 +648,7 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
||||
.vars
|
||||
.iter()
|
||||
.filter_map(|loc_pattern| match loc_pattern.value {
|
||||
ast::Pattern::Identifier(ident) => Some(ident.to_string()),
|
||||
ast::Pattern::Identifier { ident, suffixed: _ } => Some(ident.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
|
@ -5647,7 +5647,15 @@ fn value_def_from_imports<'a>(
|
||||
);
|
||||
};
|
||||
let typed_ident = typed_ident.extract_spaces().item;
|
||||
let ident = arena.alloc(typed_ident.ident.map_owned(Pattern::Identifier));
|
||||
let Loc { region, value } = typed_ident.ident;
|
||||
let ident = arena.alloc(Loc::at(
|
||||
region,
|
||||
Pattern::Identifier {
|
||||
ident: value,
|
||||
suffixed: 0,
|
||||
},
|
||||
));
|
||||
|
||||
let ann_type = arena.alloc(typed_ident.ann);
|
||||
Some(ValueDef::AnnotatedBody {
|
||||
ann_pattern: ident,
|
||||
|
@ -415,7 +415,7 @@ fn eq_tag_union_help<'a>(
|
||||
};
|
||||
|
||||
//
|
||||
// combine all the statments
|
||||
// combine all the statements
|
||||
//
|
||||
let compare_values = tag_id_a_stmt(root.arena.alloc(
|
||||
//
|
||||
|
@ -7,6 +7,7 @@ use crate::parser::ESingleQuote;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::soa::{EitherIndex, Index, Slice};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
@ -267,9 +268,6 @@ pub enum Expr<'a> {
|
||||
// Collection Literals
|
||||
List(Collection<'a, &'a Loc<Expr<'a>>>),
|
||||
|
||||
/// An expression followed by `!``
|
||||
Suffixed(&'a Expr<'a>),
|
||||
|
||||
RecordUpdate {
|
||||
update: &'a Loc<Expr<'a>>,
|
||||
fields: Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>,
|
||||
@ -289,6 +287,7 @@ pub enum Expr<'a> {
|
||||
Var {
|
||||
module_name: &'a str, // module_name will only be filled if the original Roc code stated something like `5 + SomeModule.myVar`, module_name will be blank if it was `5 + myVar`
|
||||
ident: &'a str,
|
||||
suffixed: u8, // how many `!` suffixes, for example `doTheThing!!` executes a Task that returns a Task
|
||||
},
|
||||
|
||||
Underscore(&'a str),
|
||||
@ -306,6 +305,11 @@ pub enum Expr<'a> {
|
||||
Closure(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>),
|
||||
/// Multiple defs in a row
|
||||
Defs(&'a Defs<'a>, &'a Loc<Expr<'a>>),
|
||||
|
||||
/// Used in place of an expression when the final expression is empty
|
||||
/// This may happen if the final expression is actually a suffixed statement
|
||||
EmptyDefsFinal,
|
||||
|
||||
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
Expect(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
Dbg(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
@ -341,6 +345,7 @@ pub enum Expr<'a> {
|
||||
// Problems
|
||||
MalformedIdent(&'a str, crate::ident::BadIdent),
|
||||
MalformedClosure,
|
||||
MalformedSuffixed(&'a Loc<Expr<'a>>),
|
||||
// Both operators were non-associative, e.g. (True == False == False).
|
||||
// We should tell the author to disambiguate by grouping them with parens.
|
||||
PrecedenceConflict(&'a PrecedenceConflict<'a>),
|
||||
@ -348,6 +353,110 @@ pub enum Expr<'a> {
|
||||
UnappliedRecordBuilder(&'a Loc<Expr<'a>>),
|
||||
}
|
||||
|
||||
impl Expr<'_> {
|
||||
pub fn increment_var_suffix(&mut self, count: u8) {
|
||||
match self {
|
||||
Expr::Var { suffixed, .. } => {
|
||||
*suffixed += count;
|
||||
}
|
||||
_ => {
|
||||
internal_error!("increment_var_suffix called on non-Var expression");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_region_spanning_binops(&self) -> Region {
|
||||
match self {
|
||||
Expr::BinOps(firsts, last) => {
|
||||
let mut region = last.region;
|
||||
|
||||
for (loc_expr, _) in firsts.iter() {
|
||||
region = Region::span_across(&loc_expr.region, ®ion);
|
||||
}
|
||||
|
||||
region
|
||||
}
|
||||
_ => internal_error!("other expr types not supported"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_loc_exprs_around<'a>(
|
||||
items: &'a [&Loc<Expr<'a>>],
|
||||
index: usize,
|
||||
) -> (&'a [&'a Loc<Expr<'a>>], &'a [&'a Loc<Expr<'a>>]) {
|
||||
let (before, rest) = items.split_at(index);
|
||||
let after = &rest[1..]; // Skip the index element
|
||||
|
||||
(before, after)
|
||||
}
|
||||
|
||||
pub fn is_loc_expr_suffixed(loc_expr: &Loc<Expr>) -> bool {
|
||||
match loc_expr.value.extract_spaces().item {
|
||||
// expression without arguments, `read!`
|
||||
Expr::Var { suffixed, .. } => suffixed > 0,
|
||||
|
||||
// expression with arguments, `line! "Foo"`
|
||||
Expr::Apply(sub_loc_expr, apply_args, _) => {
|
||||
let is_function_suffixed = is_loc_expr_suffixed(sub_loc_expr);
|
||||
let any_args_suffixed = apply_args.iter().any(|arg| is_loc_expr_suffixed(arg));
|
||||
|
||||
any_args_suffixed || is_function_suffixed
|
||||
}
|
||||
|
||||
// expression in a pipeline, `"hi" |> say!`
|
||||
Expr::BinOps(firsts, last) => {
|
||||
let is_expr_suffixed = is_loc_expr_suffixed(last);
|
||||
let any_chain_suffixed = firsts
|
||||
.iter()
|
||||
.any(|(chain_loc_expr, _)| is_loc_expr_suffixed(chain_loc_expr));
|
||||
|
||||
is_expr_suffixed || any_chain_suffixed
|
||||
}
|
||||
|
||||
// expression in a if-then-else, `if isOk! then "ok" else doSomething!`
|
||||
Expr::If(if_thens, final_else) => {
|
||||
let any_if_thens_suffixed = if_thens.iter().any(|(if_then, else_expr)| {
|
||||
is_loc_expr_suffixed(if_then) || is_loc_expr_suffixed(else_expr)
|
||||
});
|
||||
|
||||
is_loc_expr_suffixed(final_else) || any_if_thens_suffixed
|
||||
}
|
||||
|
||||
// expression in parens `(read!)`
|
||||
Expr::ParensAround(sub_loc_expr) => {
|
||||
is_loc_expr_suffixed(&Loc::at(loc_expr.region, *sub_loc_expr))
|
||||
}
|
||||
|
||||
// expression in a closure
|
||||
Expr::Closure(_, sub_loc_expr) => is_loc_expr_suffixed(sub_loc_expr),
|
||||
|
||||
// expressions inside a Defs
|
||||
// note we ignore the final expression as it should not be suffixed
|
||||
Expr::Defs(defs, _) => {
|
||||
let any_defs_suffixed = defs.tags.iter().any(|tag| match tag.split() {
|
||||
Ok(_) => false,
|
||||
Err(value_index) => match defs.value_defs[value_index.index()] {
|
||||
ValueDef::Body(_, loc_expr) => is_loc_expr_suffixed(loc_expr),
|
||||
ValueDef::AnnotatedBody { body_expr, .. } => is_loc_expr_suffixed(body_expr),
|
||||
_ => false,
|
||||
},
|
||||
});
|
||||
|
||||
any_defs_suffixed
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_around<T>(items: &[T], target: usize) -> (&[T], &[T]) {
|
||||
let (before, rest) = items.split_at(target);
|
||||
let after = &rest[1..];
|
||||
|
||||
(before, after)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct PrecedenceConflict<'a> {
|
||||
pub whole_region: Region,
|
||||
@ -458,6 +567,18 @@ pub enum ValueDef<'a> {
|
||||
condition: &'a Loc<Expr<'a>>,
|
||||
preceding_comment: Region,
|
||||
},
|
||||
|
||||
Stmt(&'a Loc<Expr<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> ValueDef<'a> {
|
||||
pub fn replace_expr(&mut self, new_expr: &'a Loc<Expr<'a>>) {
|
||||
match self {
|
||||
ValueDef::Body(_, expr) => *expr = new_expr,
|
||||
ValueDef::AnnotatedBody { body_expr, .. } => *body_expr = new_expr,
|
||||
_ => internal_error!("replacing expr in unsupported ValueDef"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
@ -487,6 +608,16 @@ impl<'a> Defs<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_value_defs(&self) -> impl Iterator<Item = (usize, &ValueDef<'a>)> {
|
||||
self.tags
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(tag_index, tag)| match tag.split() {
|
||||
Ok(_) => None,
|
||||
Err(value_index) => Some((tag_index, &self.value_defs[value_index.index()])),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<Result<&TypeDef<'a>, &ValueDef<'a>>> {
|
||||
self.tags.last().map(|tag| match tag.split() {
|
||||
Ok(type_index) => Ok(&self.type_defs[type_index.index()]),
|
||||
@ -494,6 +625,47 @@ impl<'a> Defs<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
// We could have a type annotation as the last tag,
|
||||
// this helper ensures we refer to the last value_def
|
||||
// and that we remove the correct tag
|
||||
pub fn last_value_suffixed(&self) -> Option<(Self, &'a Loc<Expr<'a>>)> {
|
||||
let value_indexes =
|
||||
self.tags
|
||||
.clone()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(tag_index, tag)| match tag.split() {
|
||||
Ok(_) => None,
|
||||
Err(value_index) => Some((tag_index, value_index.index())),
|
||||
});
|
||||
|
||||
if let Some((tag_index, value_index)) = value_indexes.last() {
|
||||
match self.value_defs[value_index] {
|
||||
ValueDef::Body(
|
||||
Loc {
|
||||
value: Pattern::RecordDestructure(collection),
|
||||
..
|
||||
},
|
||||
loc_expr,
|
||||
) if collection.is_empty() && is_loc_expr_suffixed(loc_expr) => {
|
||||
let mut new_defs = self.clone();
|
||||
new_defs.remove_value_def(tag_index);
|
||||
|
||||
return Some((new_defs, loc_expr));
|
||||
}
|
||||
ValueDef::Stmt(loc_expr) if is_loc_expr_suffixed(loc_expr) => {
|
||||
let mut new_defs = self.clone();
|
||||
new_defs.remove_value_def(tag_index);
|
||||
|
||||
return Some((new_defs, loc_expr));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn remove_value_def(&mut self, index: usize) {
|
||||
match self
|
||||
.tags
|
||||
@ -567,6 +739,7 @@ impl<'a> Defs<'a> {
|
||||
self.push_def_help(tag, region, spaces_before, spaces_after)
|
||||
}
|
||||
|
||||
/// Replace the `value_def` at the given index
|
||||
pub fn replace_with_value_def(
|
||||
&mut self,
|
||||
index: usize,
|
||||
@ -592,51 +765,81 @@ impl<'a> Defs<'a> {
|
||||
self.push_def_help(tag, region, spaces_before, spaces_after)
|
||||
}
|
||||
|
||||
// Find the first definition that is a Apply Suffixed
|
||||
// We need the tag_index so we can use it to remove the value
|
||||
// We need the value index to know if it is the first
|
||||
pub fn search_suffixed_defs(&self) -> Option<(usize, usize)> {
|
||||
for (tag_index, tag) in self.tags.iter().enumerate() {
|
||||
if let Err(value_index) = tag.split() {
|
||||
let index = value_index.index();
|
||||
/// Split the defs around a given target index
|
||||
///
|
||||
/// This is useful for unwrapping suffixed `!`
|
||||
pub fn split_defs_around(&self, target: usize) -> SplitDefsAround<'a> {
|
||||
let mut before = Defs::default();
|
||||
let mut after = Defs::default();
|
||||
|
||||
if let ValueDef::Body(_, expr) = &self.value_defs[index] {
|
||||
// The Suffixed has arguments applied e.g. `Stdout.line! "Hello World"`
|
||||
if let Expr::Apply(sub_expr, _, _) = expr.value {
|
||||
if let Expr::Suffixed(_) = sub_expr.value {
|
||||
return Some((tag_index, index));
|
||||
for (tag_index, tag) in self.tags.iter().enumerate() {
|
||||
let region = self.regions[tag_index];
|
||||
let space_before = {
|
||||
let start = self.space_before[tag_index].start();
|
||||
let len = self.space_before[tag_index].len();
|
||||
|
||||
&self.spaces[start..(start + len)]
|
||||
};
|
||||
let space_after = {
|
||||
let start = self.space_after[tag_index].start();
|
||||
let len = self.space_after[tag_index].len();
|
||||
|
||||
&self.spaces[start..(start + len)]
|
||||
};
|
||||
|
||||
match tag.split() {
|
||||
Ok(type_def_index) => {
|
||||
let type_def = self.type_defs[type_def_index.index()];
|
||||
|
||||
match tag_index.cmp(&target) {
|
||||
std::cmp::Ordering::Less => {
|
||||
// before
|
||||
let type_def_index = Index::push_new(&mut before.type_defs, type_def);
|
||||
let tag = EitherIndex::from_left(type_def_index);
|
||||
before.push_def_help(tag, region, space_before, space_after);
|
||||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
// after
|
||||
let type_def_index = Index::push_new(&mut after.type_defs, type_def);
|
||||
let tag = EitherIndex::from_left(type_def_index);
|
||||
after.push_def_help(tag, region, space_before, space_after);
|
||||
}
|
||||
std::cmp::Ordering::Equal => {
|
||||
// target, do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(value_def_index) => {
|
||||
let value_def = self.value_defs[value_def_index.index()];
|
||||
|
||||
// The Suffixed has NO arguments applied e.g. `Stdin.line!`
|
||||
if let Expr::Suffixed(_) = expr.value {
|
||||
return Some((tag_index, index));
|
||||
match tag_index.cmp(&target) {
|
||||
std::cmp::Ordering::Less => {
|
||||
// before
|
||||
let new_value_def_index =
|
||||
Index::push_new(&mut before.value_defs, value_def);
|
||||
let tag = EitherIndex::from_right(new_value_def_index);
|
||||
before.push_def_help(tag, region, space_before, space_after);
|
||||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
// after
|
||||
let new_value_def_index =
|
||||
Index::push_new(&mut after.value_defs, value_def);
|
||||
let tag = EitherIndex::from_right(new_value_def_index);
|
||||
after.push_def_help(tag, region, space_before, space_after);
|
||||
}
|
||||
std::cmp::Ordering::Equal => {
|
||||
// target, do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// For desugaring Suffixed Defs we need to split the defs around the Suffixed value
|
||||
pub fn split_values_either_side_of(&self, index: usize) -> SplitDefsAround {
|
||||
let mut before = self.clone();
|
||||
let mut after = self.clone();
|
||||
|
||||
before.tags = self.tags[0..index].to_vec();
|
||||
|
||||
if index >= self.tags.len() {
|
||||
after.tags = self.tags.clone();
|
||||
after.tags.clear();
|
||||
} else {
|
||||
after.tags = self.tags[(index + 1)..].to_vec();
|
||||
}
|
||||
|
||||
SplitDefsAround { before, after }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SplitDefsAround<'a> {
|
||||
pub before: Defs<'a>,
|
||||
pub after: Defs<'a>,
|
||||
@ -871,7 +1074,15 @@ impl<'a> PatternAs<'a> {
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Pattern<'a> {
|
||||
// Identifier
|
||||
Identifier(&'a str),
|
||||
Identifier {
|
||||
ident: &'a str,
|
||||
suffixed: u8,
|
||||
},
|
||||
QualifiedIdentifier {
|
||||
module_name: &'a str,
|
||||
ident: &'a str,
|
||||
suffixed: u8,
|
||||
},
|
||||
|
||||
Tag(&'a str),
|
||||
|
||||
@ -923,10 +1134,6 @@ pub enum Pattern<'a> {
|
||||
// Malformed
|
||||
Malformed(&'a str),
|
||||
MalformedIdent(&'a str, crate::ident::BadIdent),
|
||||
QualifiedIdentifier {
|
||||
module_name: &'a str,
|
||||
ident: &'a str,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
@ -992,11 +1199,22 @@ impl<'a> Pattern<'a> {
|
||||
// { x, y } : { x : Int, y ? Bool }
|
||||
// { x, y ? False } = rec
|
||||
OptionalField(x, _) => match other {
|
||||
Identifier(y) | OptionalField(y, _) => x == y,
|
||||
Identifier {
|
||||
ident: y,
|
||||
suffixed: 0,
|
||||
}
|
||||
| OptionalField(y, _) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Identifier(x) => match other {
|
||||
Identifier(y) | OptionalField(y, _) => x == y,
|
||||
Identifier {
|
||||
ident: x,
|
||||
suffixed: a,
|
||||
} => match other {
|
||||
Identifier {
|
||||
ident: y,
|
||||
suffixed: b,
|
||||
} => x == y && a == b,
|
||||
OptionalField(y, _) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
NumLiteral(x) => {
|
||||
@ -1057,13 +1275,15 @@ impl<'a> Pattern<'a> {
|
||||
QualifiedIdentifier {
|
||||
module_name: a,
|
||||
ident: x,
|
||||
suffixed: i,
|
||||
} => {
|
||||
if let QualifiedIdentifier {
|
||||
module_name: b,
|
||||
ident: y,
|
||||
suffixed: j,
|
||||
} = other
|
||||
{
|
||||
a == b && x == y
|
||||
a == b && x == y && i == j
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@ -1128,6 +1348,15 @@ impl<'a> Pattern<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// used to check if a pattern is suffixed to report as an error
|
||||
pub fn is_suffixed(&self) -> bool {
|
||||
match self {
|
||||
Pattern::Identifier { suffixed, .. } => *suffixed > 0,
|
||||
Pattern::QualifiedIdentifier { suffixed, .. } => *suffixed > 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Collection<'a, T> {
|
||||
@ -1384,11 +1613,13 @@ impl<'a> Expr<'a> {
|
||||
pub const REPL_OPAQUE_FUNCTION: Self = Expr::Var {
|
||||
module_name: "",
|
||||
ident: "<function>",
|
||||
suffixed: 0,
|
||||
};
|
||||
|
||||
pub const REPL_RUNTIME_CRASH: Self = Expr::Var {
|
||||
module_name: "",
|
||||
ident: "*",
|
||||
suffixed: 0,
|
||||
};
|
||||
|
||||
pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> {
|
||||
@ -1617,6 +1848,7 @@ impl<'a> Malformed for Expr<'a> {
|
||||
OpaqueRef(_) |
|
||||
SingleQuote(_) | // This is just a &str - not a bunch of segments
|
||||
IngestedFile(_, _) |
|
||||
EmptyDefsFinal |
|
||||
Crash => false,
|
||||
|
||||
Str(inner) => inner.is_malformed(),
|
||||
@ -1650,10 +1882,10 @@ impl<'a> Malformed for Expr<'a> {
|
||||
|
||||
MalformedIdent(_, _) |
|
||||
MalformedClosure |
|
||||
MalformedSuffixed(..) |
|
||||
PrecedenceConflict(_) |
|
||||
MultipleRecordBuilders(_) |
|
||||
UnappliedRecordBuilder(_) => true,
|
||||
Suffixed(expr) => expr.is_malformed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1748,7 +1980,7 @@ impl<'a> Malformed for Pattern<'a> {
|
||||
use Pattern::*;
|
||||
|
||||
match self {
|
||||
Identifier(_) |
|
||||
Identifier{ .. } |
|
||||
Tag(_) |
|
||||
OpaqueRef(_) => false,
|
||||
Apply(func, args) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()),
|
||||
@ -1888,6 +2120,7 @@ impl<'a> Malformed for ValueDef<'a> {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => condition.is_malformed(),
|
||||
ValueDef::Stmt(loc_expr) => loc_expr.is_malformed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,12 +197,17 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
pub fn check_indent<'a, E>(indent_problem: fn(Position) -> E) -> impl Parser<'a, (), E>
|
||||
pub fn check_indent<'a, E>(
|
||||
indent_problem: fn(Position) -> E,
|
||||
inside_suffixed_statement: bool,
|
||||
) -> impl Parser<'a, (), E>
|
||||
where
|
||||
E: 'a,
|
||||
{
|
||||
let extra_spaces = if inside_suffixed_statement { 1 } else { 0 };
|
||||
|
||||
move |_, state: State<'a>, min_indent: u32| {
|
||||
if state.column() >= min_indent {
|
||||
if state.column() >= (min_indent + extra_spaces) {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((NoProgress, indent_problem(state.pos())))
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::ast::{
|
||||
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements,
|
||||
ImplementsAbilities, Pattern, RecordBuilderField, Spaceable, Spaces, TypeAnnotation, TypeDef,
|
||||
TypeHeader, ValueDef,
|
||||
is_loc_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
|
||||
Implements, ImplementsAbilities, Pattern, RecordBuilderField, Spaceable, Spaces,
|
||||
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
|
||||
@ -67,6 +67,20 @@ pub struct ExprParseOptions {
|
||||
///
|
||||
/// > Just foo if foo == 2 -> ...
|
||||
pub check_for_arrow: bool,
|
||||
|
||||
/// Check for a suffixed expression, if we find one then
|
||||
/// subsequent parsing for this expression should have an increased
|
||||
/// indent, this is so we can distinguish between the end of the
|
||||
/// statement and the next expression.
|
||||
pub suffixed_found: bool,
|
||||
}
|
||||
|
||||
impl ExprParseOptions {
|
||||
pub fn set_suffixed_found(&self) -> Self {
|
||||
let mut new = *self;
|
||||
new.suffixed_found = true;
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
@ -321,24 +335,18 @@ fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, E
|
||||
|
||||
fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
line_min_indent(move |arena, state: State<'a>, min_indent: u32| {
|
||||
let (_, expr, state) = loc_possibly_negative_or_negated_term(options)
|
||||
.parse(arena, state, min_indent)
|
||||
.map(|(progress, expr, state)| {
|
||||
// If the next thing after the expression is a `!`, then it's Suffixed
|
||||
if state.bytes().starts_with(b"!") {
|
||||
(
|
||||
progress,
|
||||
Loc::at(expr.region, Expr::Suffixed(arena.alloc(expr.value))),
|
||||
state.advance(1),
|
||||
)
|
||||
} else {
|
||||
(progress, expr, state)
|
||||
}
|
||||
})?;
|
||||
let (_, expr, state) =
|
||||
loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?;
|
||||
|
||||
let initial_state = state.clone();
|
||||
let end = state.pos();
|
||||
|
||||
let new_options = if is_loc_expr_suffixed(&expr) {
|
||||
options.set_suffixed_found()
|
||||
} else {
|
||||
options
|
||||
};
|
||||
|
||||
match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) {
|
||||
Err((_, _)) => Ok((MadeProgress, expr.value, state)),
|
||||
Ok((_, spaces_before_op, state)) => {
|
||||
@ -350,7 +358,31 @@ fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a
|
||||
end,
|
||||
};
|
||||
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state, initial_state)
|
||||
match parse_expr_end(
|
||||
min_indent,
|
||||
new_options,
|
||||
expr_state,
|
||||
arena,
|
||||
state,
|
||||
initial_state,
|
||||
) {
|
||||
Err(err) => Err(err),
|
||||
Ok((progress, expr, new_state)) => {
|
||||
// We need to check if we have just parsed a suffixed statement,
|
||||
// if so, this is a defs node.
|
||||
if is_loc_expr_suffixed(&Loc::at_zero(expr)) {
|
||||
let def_region = Region::new(end, new_state.pos());
|
||||
let value_def = ValueDef::Stmt(arena.alloc(Loc::at(def_region, expr)));
|
||||
|
||||
let mut defs = Defs::default();
|
||||
defs.push_value_def(value_def, def_region, &[], &[]);
|
||||
|
||||
return parse_defs_expr(options, min_indent, defs, arena, new_state);
|
||||
} else {
|
||||
Ok((progress, expr, new_state))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -410,6 +442,7 @@ impl<'a> ExprState<'a> {
|
||||
} else if !self.expr.value.is_tag()
|
||||
&& !self.expr.value.is_opaque()
|
||||
&& !self.arguments.is_empty()
|
||||
&& !is_loc_expr_suffixed(&self.expr)
|
||||
{
|
||||
let region = Region::across_all(self.arguments.iter().map(|v| &v.region));
|
||||
|
||||
@ -623,8 +656,26 @@ pub fn parse_single_def<'a>(
|
||||
}
|
||||
}
|
||||
Err((MadeProgress, _)) => {
|
||||
// a hacky way to get expression-based error messages. TODO fix this
|
||||
Ok((NoProgress, None, initial))
|
||||
// Try to parse as a Statement
|
||||
match parse_statement_inside_def(
|
||||
arena,
|
||||
initial.clone(),
|
||||
min_indent,
|
||||
options,
|
||||
start,
|
||||
spaces_before_current_start,
|
||||
// TODO including spaces_before_current here doubles things up
|
||||
&[],
|
||||
|_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) },
|
||||
) {
|
||||
Ok((_, Some(single_def), state)) => match single_def.type_or_value {
|
||||
Either::Second(ValueDef::Stmt(loc_expr)) if is_loc_expr_suffixed(loc_expr) => {
|
||||
Ok((MadeProgress, Some(single_def), state))
|
||||
}
|
||||
_ => Ok((NoProgress, None, initial)), // a hacky way to get expression-based error messages. TODO fix this
|
||||
},
|
||||
_ => Ok((NoProgress, None, initial)), // a hacky way to get expression-based error messages. TODO fix this
|
||||
}
|
||||
}
|
||||
Ok((_, loc_pattern, state)) => {
|
||||
// First let's check whether this is an ability definition.
|
||||
@ -666,185 +717,291 @@ pub fn parse_single_def<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, this is a def or alias.
|
||||
match operator().parse(arena, state, min_indent) {
|
||||
Ok((_, BinOp::Assignment, state)) => {
|
||||
let parse_def_expr = space0_before_e(
|
||||
increment_min_indent(expr_start(options)),
|
||||
EExpr::IndentEnd,
|
||||
);
|
||||
// This may be a def or alias.
|
||||
let operator_result = operator().parse(arena, state.clone(), min_indent);
|
||||
|
||||
let (_, loc_def_expr, state) =
|
||||
parse_def_expr.parse(arena, state, min_indent)?;
|
||||
let value_def =
|
||||
ValueDef::Body(arena.alloc(loc_pattern), &*arena.alloc(loc_def_expr));
|
||||
let region = Region::span_across(&loc_pattern.region, &loc_def_expr.region);
|
||||
if let Ok((_, BinOp::Assignment, operator_result_state)) = operator_result {
|
||||
return parse_single_def_assignment(
|
||||
options,
|
||||
// to support statements we have to increase the indent here so that we can parse a child def
|
||||
// within a def and still continue to parse the final expression for this def
|
||||
// e.g.
|
||||
// main =
|
||||
// Stdout.line! "Bar"
|
||||
// a=Stdout.line! "Foo"
|
||||
// Task.ok {}
|
||||
&operator_result_state.line_indent() + 1,
|
||||
arena,
|
||||
operator_result_state,
|
||||
loc_pattern,
|
||||
spaces_before_current,
|
||||
);
|
||||
};
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
))
|
||||
}
|
||||
Ok((_, BinOp::IsAliasType, state)) => {
|
||||
// the increment_min_indent here is probably _wrong_, since alias_signature_with_space_before does
|
||||
// that internally.
|
||||
// TODO: re-evaluate this
|
||||
let parser = increment_min_indent(alias_signature_with_space_before());
|
||||
let (_, ann_type, state) = parser.parse(arena, state, min_indent)?;
|
||||
let region = Region::span_across(&loc_pattern.region, &ann_type.region);
|
||||
if let Ok((_, BinOp::IsAliasType, state)) = operator_result {
|
||||
// the increment_min_indent here is probably _wrong_, since alias_signature_with_space_before does
|
||||
// that internally.
|
||||
// TODO: re-evaluate this
|
||||
let parser = increment_min_indent(alias_signature_with_space_before());
|
||||
let (_, ann_type, state) = parser.parse(arena, state, min_indent)?;
|
||||
let region = Region::span_across(&loc_pattern.region, &ann_type.region);
|
||||
|
||||
match &loc_pattern.value.extract_spaces().item {
|
||||
Pattern::Apply(
|
||||
Loc {
|
||||
value: Pattern::Tag(name),
|
||||
..
|
||||
},
|
||||
alias_arguments,
|
||||
) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: alias_arguments,
|
||||
};
|
||||
match &loc_pattern.value.extract_spaces().item {
|
||||
Pattern::Apply(
|
||||
Loc {
|
||||
value: Pattern::Tag(name),
|
||||
..
|
||||
},
|
||||
alias_arguments,
|
||||
) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: alias_arguments,
|
||||
};
|
||||
|
||||
let type_def = TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
};
|
||||
let type_def = TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
};
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::First(type_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
))
|
||||
}
|
||||
Pattern::Tag(name) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: pattern_arguments,
|
||||
};
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::First(type_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
));
|
||||
}
|
||||
Pattern::Tag(name) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: pattern_arguments,
|
||||
};
|
||||
|
||||
let type_def = TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
};
|
||||
let type_def = TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
};
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::First(type_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
let value_def = ValueDef::Annotation(loc_pattern, ann_type);
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::First(type_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
let value_def = ValueDef::Annotation(loc_pattern, ann_type);
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
))
|
||||
}
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok((_, BinOp::IsOpaqueType, state)) => {
|
||||
let (_, (signature, derived), state) =
|
||||
opaque_signature_with_space_before().parse(arena, state, min_indent + 1)?;
|
||||
let region = Region::span_across(&loc_pattern.region, &signature.region);
|
||||
};
|
||||
|
||||
match &loc_pattern.value.extract_spaces().item {
|
||||
Pattern::Apply(
|
||||
Loc {
|
||||
value: Pattern::Tag(name),
|
||||
..
|
||||
},
|
||||
alias_arguments,
|
||||
) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: alias_arguments,
|
||||
};
|
||||
if let Ok((_, BinOp::IsOpaqueType, state)) = operator_result {
|
||||
let (_, (signature, derived), state) =
|
||||
opaque_signature_with_space_before().parse(arena, state, min_indent + 1)?;
|
||||
let region = Region::span_across(&loc_pattern.region, &signature.region);
|
||||
|
||||
let type_def = TypeDef::Opaque {
|
||||
header,
|
||||
typ: signature,
|
||||
derived,
|
||||
};
|
||||
match &loc_pattern.value.extract_spaces().item {
|
||||
Pattern::Apply(
|
||||
Loc {
|
||||
value: Pattern::Tag(name),
|
||||
..
|
||||
},
|
||||
alias_arguments,
|
||||
) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: alias_arguments,
|
||||
};
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::First(type_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
))
|
||||
}
|
||||
Pattern::Tag(name) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: pattern_arguments,
|
||||
};
|
||||
let type_def = TypeDef::Opaque {
|
||||
header,
|
||||
typ: signature,
|
||||
derived,
|
||||
};
|
||||
|
||||
let type_def = TypeDef::Opaque {
|
||||
header,
|
||||
typ: signature,
|
||||
derived,
|
||||
};
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::First(type_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
));
|
||||
}
|
||||
Pattern::Tag(name) => {
|
||||
let name = Loc::at(loc_pattern.region, *name);
|
||||
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
|
||||
let header = TypeHeader {
|
||||
name,
|
||||
vars: pattern_arguments,
|
||||
};
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::First(type_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
let value_def = ValueDef::Annotation(loc_pattern, signature);
|
||||
let type_def = TypeDef::Opaque {
|
||||
header,
|
||||
typ: signature,
|
||||
derived,
|
||||
};
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
))
|
||||
}
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::First(type_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
let value_def = ValueDef::Annotation(loc_pattern, signature);
|
||||
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => Ok((MadeProgress, None, initial)),
|
||||
};
|
||||
|
||||
// Otherwise try to re-parse as a Statement
|
||||
match parse_statement_inside_def(
|
||||
arena,
|
||||
initial.clone(),
|
||||
min_indent,
|
||||
options,
|
||||
start,
|
||||
spaces_before_current_start,
|
||||
// TODO figure out why including spaces_before_current here doubles things up
|
||||
&[],
|
||||
|_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) },
|
||||
) {
|
||||
Ok((_, Some(single_def), state)) => match single_def.type_or_value {
|
||||
Either::Second(ValueDef::Stmt(loc_expr)) if is_loc_expr_suffixed(loc_expr) => {
|
||||
Ok((MadeProgress, Some(single_def), state))
|
||||
}
|
||||
_ => Ok((NoProgress, None, initial)),
|
||||
},
|
||||
_ => Ok((NoProgress, None, initial)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_single_def_assignment<'a>(
|
||||
options: ExprParseOptions,
|
||||
min_indent: u32,
|
||||
arena: &'a Bump,
|
||||
initial_state: State<'a>,
|
||||
def_loc_pattern: Loc<Pattern<'a>>,
|
||||
spaces_before_current: &'a [CommentOrNewline<'a>],
|
||||
) -> ParseResult<'a, Option<SingleDef<'a>>, EExpr<'a>> {
|
||||
// Try and parse the expression
|
||||
let parse_def_expr =
|
||||
space0_before_e(increment_min_indent(expr_start(options)), EExpr::IndentEnd);
|
||||
let (progress_after_first, first_loc_expr, state_after_first_expression) =
|
||||
parse_def_expr.parse(arena, initial_state, min_indent)?;
|
||||
|
||||
let region = Region::span_across(&def_loc_pattern.region, &first_loc_expr.region);
|
||||
|
||||
// If the expression is actually a suffixed statement, then we need to continue
|
||||
// to parse the rest of the expression
|
||||
if crate::ast::is_loc_expr_suffixed(&first_loc_expr) {
|
||||
let mut defs = Defs::default();
|
||||
// Take the suffixed value and make it a e.g. Body(`{}=`, Apply(Var(...)))
|
||||
// we will keep the pattern `def_loc_pattern` for the new Defs
|
||||
defs.push_value_def(
|
||||
ValueDef::Stmt(arena.alloc(first_loc_expr)),
|
||||
Region::span_across(&def_loc_pattern.region, &first_loc_expr.region),
|
||||
spaces_before_current,
|
||||
&[],
|
||||
);
|
||||
|
||||
// Try to parse the rest of the expression as multiple defs, which may contain sub-assignments
|
||||
match parse_defs_expr(
|
||||
options,
|
||||
min_indent,
|
||||
defs.clone(),
|
||||
arena,
|
||||
state_after_first_expression.clone(),
|
||||
) {
|
||||
Ok((progress_after_rest_of_def, expr, state_after_rest_of_def)) => {
|
||||
let final_loc_expr = arena.alloc(Loc::at(region, expr));
|
||||
|
||||
let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), final_loc_expr);
|
||||
|
||||
return Ok((
|
||||
progress_after_rest_of_def,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state_after_rest_of_def,
|
||||
));
|
||||
}
|
||||
Err(_) => {
|
||||
// Unable to parse more defs, continue and return the first parsed expression as a stement
|
||||
let empty_return =
|
||||
arena.alloc(Loc::at(first_loc_expr.region, Expr::EmptyDefsFinal));
|
||||
let value_def = ValueDef::Body(
|
||||
arena.alloc(def_loc_pattern),
|
||||
arena.alloc(Loc::at(
|
||||
first_loc_expr.region,
|
||||
Expr::Defs(arena.alloc(defs), empty_return),
|
||||
)),
|
||||
);
|
||||
return Ok((
|
||||
progress_after_first,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state_after_first_expression,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), arena.alloc(first_loc_expr));
|
||||
|
||||
Ok((
|
||||
progress_after_first,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
}),
|
||||
state_after_first_expression,
|
||||
))
|
||||
}
|
||||
|
||||
/// e.g. Things that can be on their own line in a def, e.g. `expect`, `expect-fx`, or `dbg`
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn parse_statement_inside_def<'a>(
|
||||
@ -878,6 +1035,7 @@ fn parse_statement_inside_def<'a>(
|
||||
}
|
||||
|
||||
let preceding_comment = Region::new(spaces_before_current_start, start);
|
||||
|
||||
let value_def = get_value_def(preceding_comment, loc_def_expr);
|
||||
|
||||
Ok((
|
||||
@ -960,6 +1118,9 @@ fn parse_defs_end<'a>(
|
||||
let mut global_state = state;
|
||||
|
||||
loop {
|
||||
// keep a copy in the event we get an EExpr::DefMissingFinalExpr
|
||||
let state_before = global_state.clone();
|
||||
|
||||
let state = global_state;
|
||||
|
||||
global_state = match parse_single_def(options, min_indent, arena, state) {
|
||||
@ -1039,11 +1200,16 @@ fn parse_defs_end<'a>(
|
||||
next_state
|
||||
}
|
||||
Ok((progress, None, s)) => return Ok((progress, defs, s)),
|
||||
Err((MadeProgress, EExpr::DefMissingFinalExpr(..)))
|
||||
| Err((MadeProgress, EExpr::DefMissingFinalExpr2(..))) => {
|
||||
return Ok((MadeProgress, defs, state_before))
|
||||
}
|
||||
Err((progress, err)) => return Err((progress, err)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SingleDef<'a> {
|
||||
pub type_or_value: Either<TypeDef<'a>, ValueDef<'a>>,
|
||||
pub region: Region,
|
||||
@ -1065,18 +1231,50 @@ fn parse_defs_expr<'a>(
|
||||
|
||||
match parse_final_expr.parse(arena, state.clone(), min_indent) {
|
||||
Err((_, fail)) => {
|
||||
return Err((
|
||||
// If the last def was a suffixed statement, assume this was
|
||||
// intentional by the application author instead of giving
|
||||
// an error.
|
||||
if let Some((new_defs, loc_ret)) = def_state.last_value_suffixed() {
|
||||
// note we check the tags here and not value_defs, as there may be redundant defs in Defs
|
||||
|
||||
let mut local_defs = new_defs.clone();
|
||||
|
||||
let last_stmt = ValueDef::Stmt(loc_ret);
|
||||
local_defs.push_value_def(last_stmt, loc_ret.region, &[], &[]);
|
||||
|
||||
//check the length of the defs we would return, if we only have one
|
||||
// we can just return the expression
|
||||
// note we use tags here, as we may have redundant defs in Defs
|
||||
if local_defs
|
||||
.tags
|
||||
.iter()
|
||||
.filter(|tag| tag.split().is_err())
|
||||
.count()
|
||||
== 1
|
||||
{
|
||||
return Ok((MadeProgress, loc_ret.value, state));
|
||||
}
|
||||
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Expr::Defs(
|
||||
arena.alloc(local_defs),
|
||||
arena.alloc(Loc::at_zero(Expr::EmptyDefsFinal)),
|
||||
),
|
||||
state,
|
||||
));
|
||||
}
|
||||
|
||||
Err((
|
||||
MadeProgress,
|
||||
EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.pos()),
|
||||
));
|
||||
}
|
||||
Ok((_, loc_ret, state)) => {
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)),
|
||||
state,
|
||||
));
|
||||
))
|
||||
}
|
||||
Ok((_, loc_ret, state)) => Ok((
|
||||
MadeProgress,
|
||||
Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)),
|
||||
state,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1633,8 +1831,51 @@ fn parse_expr_operator<'a>(
|
||||
expr_state.end = new_end;
|
||||
expr_state.spaces_after = spaces;
|
||||
|
||||
// TODO new start?
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state, initial_state)
|
||||
let new_options = if is_loc_expr_suffixed(&new_expr) {
|
||||
options.set_suffixed_found()
|
||||
} else {
|
||||
options
|
||||
};
|
||||
|
||||
match parse_expr_end(
|
||||
min_indent,
|
||||
new_options,
|
||||
expr_state,
|
||||
arena,
|
||||
state,
|
||||
initial_state,
|
||||
) {
|
||||
Ok((progress, expr, state)) => {
|
||||
if let Expr::BinOps(..) = expr {
|
||||
let def_region = expr.get_region_spanning_binops();
|
||||
let mut new_expr = Loc::at(def_region, expr);
|
||||
|
||||
if is_loc_expr_suffixed(&new_expr) {
|
||||
// We have parsed a statement such as `"hello" |> line!`
|
||||
// put the spaces from after the operator in front of the call
|
||||
if !spaces_after_operator.is_empty() {
|
||||
new_expr = arena.alloc(expr).with_spaces_before(
|
||||
spaces_after_operator,
|
||||
def_region,
|
||||
);
|
||||
}
|
||||
|
||||
let value_def = ValueDef::Stmt(arena.alloc(new_expr));
|
||||
|
||||
let mut defs = Defs::default();
|
||||
defs.push_value_def(value_def, def_region, &[], &[]);
|
||||
|
||||
return parse_defs_expr(
|
||||
options, min_indent, defs, arena, state,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// else return the parsed expression
|
||||
Ok((progress, expr, state))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1654,7 +1895,7 @@ fn parse_expr_end<'a>(
|
||||
initial_state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let parser = skip_first!(
|
||||
crate::blankspace::check_indent(EExpr::IndentEnd),
|
||||
crate::blankspace::check_indent(EExpr::IndentEnd, options.suffixed_found),
|
||||
loc_term_or_underscore(options)
|
||||
);
|
||||
|
||||
@ -1667,6 +1908,7 @@ fn parse_expr_end<'a>(
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: crate::keyword::IMPLEMENTS,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
@ -1715,6 +1957,12 @@ fn parse_expr_end<'a>(
|
||||
Ok((_, mut arg, state)) => {
|
||||
let new_end = state.pos();
|
||||
|
||||
let new_options = if is_loc_expr_suffixed(&arg) {
|
||||
options.set_suffixed_found()
|
||||
} else {
|
||||
options
|
||||
};
|
||||
|
||||
// now that we have `function arg1 ... <spaces> argn`, attach the spaces to the `argn`
|
||||
if !expr_state.spaces_after.is_empty() {
|
||||
arg = arena
|
||||
@ -1739,7 +1987,14 @@ fn parse_expr_end<'a>(
|
||||
expr_state.end = new_end;
|
||||
expr_state.spaces_after = new_spaces;
|
||||
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state, initial_state)
|
||||
parse_expr_end(
|
||||
min_indent,
|
||||
new_options,
|
||||
expr_state,
|
||||
arena,
|
||||
state,
|
||||
initial_state,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1855,6 +2110,7 @@ pub fn loc_expr<'a>(accept_multi_backpassing: bool) -> impl Parser<'a, Loc<Expr<
|
||||
expr_start(ExprParseOptions {
|
||||
accept_multi_backpassing,
|
||||
check_for_arrow: true,
|
||||
suffixed_found: false,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1889,11 +2145,19 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||
}
|
||||
|
||||
let mut pat = match expr.item {
|
||||
Expr::Var { module_name, ident } => {
|
||||
Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
} => {
|
||||
if module_name.is_empty() {
|
||||
Pattern::Identifier(ident)
|
||||
Pattern::Identifier { ident, suffixed }
|
||||
} else {
|
||||
Pattern::QualifiedIdentifier { module_name, ident }
|
||||
Pattern::QualifiedIdentifier {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Underscore(opt_name) => Pattern::Underscore(opt_name),
|
||||
@ -1921,7 +2185,8 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||
Expr::SpaceBefore(..)
|
||||
| Expr::SpaceAfter(..)
|
||||
| Expr::ParensAround(..)
|
||||
| Expr::RecordBuilder(..) => unreachable!(),
|
||||
| Expr::RecordBuilder(..)
|
||||
| Expr::EmptyDefsFinal => unreachable!(),
|
||||
|
||||
Expr::Record(fields) => {
|
||||
let patterns = fields.map_items_result(arena, |loc_assigned_field| {
|
||||
@ -1967,6 +2232,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||
| Expr::Dbg(_, _)
|
||||
| Expr::LowLevelDbg(_, _, _)
|
||||
| Expr::MalformedClosure
|
||||
| Expr::MalformedSuffixed(..)
|
||||
| Expr::PrecedenceConflict { .. }
|
||||
| Expr::MultipleRecordBuilders { .. }
|
||||
| Expr::UnappliedRecordBuilder { .. }
|
||||
@ -1977,7 +2243,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||
Expr::Str(string) => Pattern::StrLiteral(string),
|
||||
Expr::SingleQuote(string) => Pattern::SingleQuote(string),
|
||||
Expr::MalformedIdent(string, problem) => Pattern::MalformedIdent(string, problem),
|
||||
Expr::Suffixed(_) => todo!(),
|
||||
};
|
||||
|
||||
// Now we re-add the spaces
|
||||
@ -2027,7 +2292,10 @@ fn assigned_expr_field_to_pattern_help<'a>(
|
||||
)
|
||||
}
|
||||
}
|
||||
AssignedField::LabelOnly(name) => Pattern::Identifier(name.value),
|
||||
AssignedField::LabelOnly(name) => Pattern::Identifier {
|
||||
ident: name.value,
|
||||
suffixed: 0,
|
||||
},
|
||||
AssignedField::SpaceBefore(nested, spaces) => Pattern::SpaceBefore(
|
||||
arena.alloc(assigned_expr_field_to_pattern_help(arena, nested)?),
|
||||
spaces,
|
||||
@ -2050,6 +2318,7 @@ pub fn toplevel_defs<'a>() -> impl Parser<'a, Defs<'a>, EExpr<'a>> {
|
||||
let options = ExprParseOptions {
|
||||
accept_multi_backpassing: true,
|
||||
check_for_arrow: true,
|
||||
suffixed_found: false,
|
||||
};
|
||||
|
||||
let mut output = Defs::default();
|
||||
@ -2528,10 +2797,11 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
||||
// The first value in the iterator is the variable name,
|
||||
// e.g. `foo` in `foo.bar.baz`
|
||||
let mut answer = match iter.next() {
|
||||
Some(Accessor::RecordField(ident)) if suffixed => {
|
||||
Expr::Suffixed(arena.alloc(Expr::Var { module_name, ident }))
|
||||
}
|
||||
Some(Accessor::RecordField(ident)) => Expr::Var { module_name, ident },
|
||||
Some(Accessor::RecordField(ident)) => Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed,
|
||||
},
|
||||
Some(Accessor::TupleIndex(_)) => {
|
||||
// TODO: make this state impossible to represent in Ident::Access,
|
||||
// by splitting out parts[0] into a separate field with a type of `&'a str`,
|
||||
|
@ -42,7 +42,7 @@ pub enum Ident<'a> {
|
||||
Access {
|
||||
module_name: &'a str,
|
||||
parts: &'a [Accessor<'a>],
|
||||
suffixed: bool,
|
||||
suffixed: u8,
|
||||
},
|
||||
/// `.foo { foo: 42 }` or `.1 (1, 2, 3)`
|
||||
AccessorFunction(Accessor<'a>),
|
||||
@ -194,7 +194,9 @@ pub fn parse_ident<'a>(
|
||||
Ok((width, ident)) => {
|
||||
let state = advance_state!(state, width as usize)?;
|
||||
if let Ident::Access {
|
||||
module_name, parts, ..
|
||||
module_name,
|
||||
parts,
|
||||
suffixed,
|
||||
} = ident
|
||||
{
|
||||
if module_name.is_empty() {
|
||||
@ -206,21 +208,16 @@ pub fn parse_ident<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a suffixed `!` expression
|
||||
if state.bytes().starts_with(b"!") {
|
||||
if let Ident::Access {
|
||||
module_name, parts, ..
|
||||
} = ident
|
||||
{
|
||||
let new_ident = Ident::Access {
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Ident::Access {
|
||||
module_name,
|
||||
parts,
|
||||
suffixed: true,
|
||||
};
|
||||
return Ok((MadeProgress, new_ident, state.advance(1)));
|
||||
}
|
||||
suffixed,
|
||||
},
|
||||
state,
|
||||
));
|
||||
}
|
||||
|
||||
Ok((MadeProgress, ident, state))
|
||||
@ -531,10 +528,22 @@ fn chomp_identifier_chain<'a>(
|
||||
|
||||
chomped += width as usize;
|
||||
|
||||
// Parse any `!` suffixes
|
||||
let mut suffixed = 0;
|
||||
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
|
||||
if ch == '!' {
|
||||
suffixed += 1;
|
||||
chomped += width;
|
||||
} else {
|
||||
// we're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let ident = Ident::Access {
|
||||
module_name,
|
||||
parts: parts.into_bump_slice(),
|
||||
suffixed: false,
|
||||
suffixed,
|
||||
};
|
||||
|
||||
Ok((chomped as u32, ident))
|
||||
@ -563,15 +572,30 @@ fn chomp_identifier_chain<'a>(
|
||||
} else if first_is_uppercase {
|
||||
// just one segment, starting with an uppercase letter; that's a tag
|
||||
let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) };
|
||||
|
||||
Ok((chomped as u32, Ident::Tag(value)))
|
||||
} else {
|
||||
// just one segment, starting with a lowercase letter; that's a normal identifier
|
||||
let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) };
|
||||
|
||||
// Parse any `!` suffixes
|
||||
let mut suffixed = 0;
|
||||
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
|
||||
if ch == '!' {
|
||||
suffixed += 1;
|
||||
chomped += width;
|
||||
} else {
|
||||
// we're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let ident = Ident::Access {
|
||||
module_name: "",
|
||||
parts: arena.alloc([Accessor::RecordField(value)]),
|
||||
suffixed: false,
|
||||
suffixed,
|
||||
};
|
||||
|
||||
Ok((chomped as u32, ident))
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use crate::string_literal::StrLikeLiteral;
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
||||
/// Different patterns are supported in different circumstances.
|
||||
@ -49,6 +50,14 @@ pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>>
|
||||
|
||||
let pattern_state = state.clone();
|
||||
|
||||
// Return early with the suffixed statement
|
||||
match pattern.value {
|
||||
Pattern::Identifier { suffixed, .. } if suffixed > 0 => {
|
||||
return Ok((MadeProgress, pattern, pattern_state))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let (pattern_spaces, state) =
|
||||
match space0_e(EPattern::AsKeyword).parse(arena, state, min_indent) {
|
||||
Err(_) => return Ok((MadeProgress, pattern, pattern_state)),
|
||||
@ -138,7 +147,15 @@ fn loc_tag_pattern_arg<'a>(
|
||||
|
||||
let Loc { region, value } = loc_pat;
|
||||
|
||||
if stop_on_has_kw && matches!(value, Pattern::Identifier(crate::keyword::IMPLEMENTS)) {
|
||||
if stop_on_has_kw
|
||||
&& matches!(
|
||||
value,
|
||||
Pattern::Identifier {
|
||||
ident: crate::keyword::IMPLEMENTS,
|
||||
..
|
||||
}
|
||||
)
|
||||
{
|
||||
Err((NoProgress, EPattern::End(original_state.pos())))
|
||||
} else {
|
||||
Ok((
|
||||
@ -160,7 +177,10 @@ pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPatt
|
||||
|_arena, state, progress, pattern| {
|
||||
if matches!(
|
||||
pattern.value,
|
||||
Pattern::Identifier(crate::keyword::IMPLEMENTS)
|
||||
Pattern::Identifier {
|
||||
ident: crate::keyword::IMPLEMENTS,
|
||||
..
|
||||
}
|
||||
) {
|
||||
Ok((
|
||||
progress,
|
||||
@ -385,6 +405,46 @@ fn loc_ident_pattern_help<'a>(
|
||||
Ok((MadeProgress, loc_pat, state))
|
||||
}
|
||||
}
|
||||
// Parse a statement that begins with a suffixed identifier, e.g. `Stdout.line! "Hello"`
|
||||
Ident::Access {
|
||||
module_name,
|
||||
parts,
|
||||
suffixed,
|
||||
..
|
||||
} if suffixed > 0 => {
|
||||
if module_name.is_empty() && parts.len() == 1 {
|
||||
if let Accessor::RecordField(var) = &parts[0] {
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::Identifier {
|
||||
ident: var,
|
||||
suffixed,
|
||||
},
|
||||
},
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
internal_error!("unexpected suffixed TupleIndex");
|
||||
}
|
||||
} else if let Accessor::RecordField(var) = &parts[0] {
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::QualifiedIdentifier {
|
||||
module_name,
|
||||
ident: var,
|
||||
suffixed,
|
||||
},
|
||||
},
|
||||
state,
|
||||
));
|
||||
} else {
|
||||
internal_error!("unexpected suffixed TupleIndex");
|
||||
}
|
||||
}
|
||||
Ident::Access {
|
||||
module_name, parts, ..
|
||||
} => {
|
||||
@ -403,7 +463,10 @@ fn loc_ident_pattern_help<'a>(
|
||||
MadeProgress,
|
||||
Loc {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::Identifier(var),
|
||||
value: Pattern::Identifier {
|
||||
ident: var,
|
||||
suffixed: 0,
|
||||
},
|
||||
},
|
||||
state,
|
||||
));
|
||||
@ -564,9 +627,18 @@ fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>>
|
||||
None => {
|
||||
let Loc { value, region } = loc_label;
|
||||
let value = if !spaces.is_empty() {
|
||||
Pattern::SpaceAfter(arena.alloc(Pattern::Identifier(value)), spaces)
|
||||
Pattern::SpaceAfter(
|
||||
arena.alloc(Pattern::Identifier {
|
||||
ident: value,
|
||||
suffixed: 0,
|
||||
}),
|
||||
spaces,
|
||||
)
|
||||
} else {
|
||||
Pattern::Identifier(value)
|
||||
Pattern::Identifier {
|
||||
ident: value,
|
||||
suffixed: 0,
|
||||
}
|
||||
};
|
||||
|
||||
Ok((MadeProgress, Loc::at(region, value), state))
|
||||
|
@ -70,7 +70,13 @@ fn check_type_alias<'a>(
|
||||
var_names.reserve(vars.len());
|
||||
for var in vars {
|
||||
if let TypeAnnotation::BoundVariable(v) = var.value {
|
||||
var_names.push(Loc::at(var.region, Pattern::Identifier(v)));
|
||||
var_names.push(Loc::at(
|
||||
var.region,
|
||||
Pattern::Identifier {
|
||||
ident: v,
|
||||
suffixed: 0,
|
||||
},
|
||||
));
|
||||
} else {
|
||||
return Err(ETypeInlineAlias::ArgumentNotLowercase(var.region.start()));
|
||||
}
|
||||
|
@ -175,6 +175,7 @@ mod test_parse {
|
||||
let expr = arena.alloc(Var {
|
||||
module_name: "",
|
||||
ident: "name",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
bumpalo::vec![in arena;
|
||||
@ -191,6 +192,7 @@ mod test_parse {
|
||||
let expr = arena.alloc(Var {
|
||||
module_name: "",
|
||||
ident: "name",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
bumpalo::vec![in arena;
|
||||
@ -236,6 +238,7 @@ mod test_parse {
|
||||
let expr = arena.alloc(Var {
|
||||
module_name: "",
|
||||
ident: "name",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
bumpalo::vec![in arena;
|
||||
@ -251,11 +254,13 @@ mod test_parse {
|
||||
let expr1 = arena.alloc(Var {
|
||||
module_name: "",
|
||||
ident: "name",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
let expr2 = arena.alloc(Var {
|
||||
module_name: "",
|
||||
ident: "project",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
bumpalo::vec![in arena;
|
||||
@ -276,11 +281,13 @@ mod test_parse {
|
||||
let expr1 = arena.alloc(Var {
|
||||
module_name: "",
|
||||
ident: "name",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
let expr2 = arena.alloc(Var {
|
||||
module_name: "",
|
||||
ident: "project",
|
||||
suffixed: 0,
|
||||
});
|
||||
|
||||
bumpalo::vec![in arena;
|
||||
|
@ -356,6 +356,7 @@ impl Problem {
|
||||
| Problem::RuntimeError(RuntimeError::MalformedIdentifier(_, _, region))
|
||||
| Problem::RuntimeError(RuntimeError::MalformedTypeName(_, region))
|
||||
| Problem::RuntimeError(RuntimeError::MalformedClosure(region))
|
||||
| Problem::RuntimeError(RuntimeError::MalformedSuffixed(region))
|
||||
| Problem::RuntimeError(RuntimeError::InvalidRecordUpdate { region })
|
||||
| Problem::RuntimeError(RuntimeError::InvalidFloat(_, region, _))
|
||||
| Problem::RuntimeError(RuntimeError::InvalidInt(_, _, region, _))
|
||||
@ -612,6 +613,8 @@ pub enum RuntimeError {
|
||||
|
||||
MultipleRecordBuilders(Region),
|
||||
UnappliedRecordBuilder(Region),
|
||||
|
||||
MalformedSuffixed(Region),
|
||||
}
|
||||
|
||||
impl RuntimeError {
|
||||
@ -645,6 +648,7 @@ impl RuntimeError {
|
||||
| RuntimeError::MalformedIdentifier(_, _, region)
|
||||
| RuntimeError::MalformedTypeName(_, region)
|
||||
| RuntimeError::MalformedClosure(region)
|
||||
| RuntimeError::MalformedSuffixed(region)
|
||||
| RuntimeError::InvalidRecordUpdate { region }
|
||||
| RuntimeError::InvalidFloat(_, region, _)
|
||||
| RuntimeError::InvalidInt(_, _, region, _)
|
||||
|
@ -2,6 +2,7 @@ When(
|
||||
@5-6 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
|
@ -2,6 +2,7 @@ When(
|
||||
@5-6 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
|
@ -4,6 +4,7 @@ BinOps(
|
||||
@0-1 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@2-3 Plus,
|
||||
),
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-3 Identifier(
|
||||
"foo",
|
||||
),
|
||||
@0-3 Identifier {
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
@6-27 TagUnion {
|
||||
ext: None,
|
||||
tags: [
|
||||
@ -40,9 +41,10 @@ Defs(
|
||||
},
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-3 Identifier(
|
||||
"foo",
|
||||
),
|
||||
ann_pattern: @0-3 Identifier {
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
ann_type: @6-27 TagUnion {
|
||||
ext: None,
|
||||
tags: [
|
||||
@ -63,9 +65,10 @@ Defs(
|
||||
],
|
||||
},
|
||||
comment: None,
|
||||
body_pattern: @28-31 Identifier(
|
||||
"foo",
|
||||
),
|
||||
body_pattern: @28-31 Identifier {
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
body_expr: @34-38 Tag(
|
||||
"True",
|
||||
),
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-3 Identifier(
|
||||
"foo",
|
||||
),
|
||||
@0-3 Identifier {
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
@6-28 TagUnion {
|
||||
ext: Some(
|
||||
@27-28 Wildcard,
|
||||
@ -42,9 +43,10 @@ Defs(
|
||||
},
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-3 Identifier(
|
||||
"foo",
|
||||
),
|
||||
ann_pattern: @0-3 Identifier {
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
ann_type: @6-28 TagUnion {
|
||||
ext: Some(
|
||||
@27-28 Wildcard,
|
||||
@ -67,9 +69,10 @@ Defs(
|
||||
],
|
||||
},
|
||||
comment: None,
|
||||
body_pattern: @29-32 Identifier(
|
||||
"foo",
|
||||
),
|
||||
body_pattern: @29-32 Identifier {
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
body_expr: @35-39 Tag(
|
||||
"True",
|
||||
),
|
||||
|
@ -18,12 +18,14 @@ Defs(
|
||||
Annotation(
|
||||
@0-8 RecordDestructure(
|
||||
[
|
||||
@2-3 Identifier(
|
||||
"x",
|
||||
),
|
||||
@5-7 Identifier(
|
||||
"y",
|
||||
),
|
||||
@2-3 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@5-7 Identifier {
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
@11-14 Apply(
|
||||
@ -35,12 +37,14 @@ Defs(
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-8 RecordDestructure(
|
||||
[
|
||||
@2-3 Identifier(
|
||||
"x",
|
||||
),
|
||||
@5-7 Identifier(
|
||||
"y",
|
||||
),
|
||||
@2-3 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@5-7 Identifier {
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
ann_type: @11-14 Apply(
|
||||
@ -51,12 +55,14 @@ Defs(
|
||||
comment: None,
|
||||
body_pattern: @15-23 RecordDestructure(
|
||||
[
|
||||
@17-18 Identifier(
|
||||
"x",
|
||||
),
|
||||
@20-21 Identifier(
|
||||
"y",
|
||||
),
|
||||
@17-18 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@20-21 Identifier {
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
body_expr: @26-49 Record(
|
||||
@ -86,6 +92,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -18,9 +18,10 @@ Defs(
|
||||
header: TypeHeader {
|
||||
name: @0-6 "UserId",
|
||||
vars: [
|
||||
@7-8 Identifier(
|
||||
"x",
|
||||
),
|
||||
@7-8 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
ann: @11-25 TagUnion {
|
||||
@ -47,9 +48,10 @@ Defs(
|
||||
"UserId",
|
||||
),
|
||||
[
|
||||
@7-8 Identifier(
|
||||
"x",
|
||||
),
|
||||
@7-8 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
ann_type: @11-25 TagUnion {
|
||||
@ -73,9 +75,10 @@ Defs(
|
||||
"UserId",
|
||||
),
|
||||
[
|
||||
@33-34 Identifier(
|
||||
"x",
|
||||
),
|
||||
@33-34 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
body_expr: @37-46 Apply(
|
||||
@ -96,6 +99,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -18,12 +18,14 @@ Defs(
|
||||
Annotation(
|
||||
@0-8 Tuple(
|
||||
[
|
||||
@2-3 Identifier(
|
||||
"x",
|
||||
),
|
||||
@5-6 Identifier(
|
||||
"y",
|
||||
),
|
||||
@2-3 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@5-6 Identifier {
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
@11-14 Apply(
|
||||
@ -35,12 +37,14 @@ Defs(
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-8 Tuple(
|
||||
[
|
||||
@2-3 Identifier(
|
||||
"x",
|
||||
),
|
||||
@5-6 Identifier(
|
||||
"y",
|
||||
),
|
||||
@2-3 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@5-6 Identifier {
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
ann_type: @11-14 Apply(
|
||||
@ -51,12 +55,14 @@ Defs(
|
||||
comment: None,
|
||||
body_pattern: @15-23 Tuple(
|
||||
[
|
||||
@17-18 Identifier(
|
||||
"x",
|
||||
),
|
||||
@20-21 Identifier(
|
||||
"y",
|
||||
),
|
||||
@17-18 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@20-21 Identifier {
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
body_expr: @26-41 Tuple(
|
||||
@ -78,6 +84,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -2,19 +2,23 @@ Apply(
|
||||
@0-1 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@2-3 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
suffixed: 0,
|
||||
},
|
||||
@4-5 Var {
|
||||
module_name: "",
|
||||
ident: "c",
|
||||
suffixed: 0,
|
||||
},
|
||||
@6-7 Var {
|
||||
module_name: "",
|
||||
ident: "d",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
Space,
|
||||
|
@ -2,6 +2,7 @@ Apply(
|
||||
@0-4 Var {
|
||||
module_name: "",
|
||||
ident: "whee",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@6-8 Num(
|
||||
|
@ -3,6 +3,7 @@ Apply(
|
||||
@1-5 Var {
|
||||
module_name: "",
|
||||
ident: "whee",
|
||||
suffixed: 0,
|
||||
},
|
||||
@0-1 Negate,
|
||||
),
|
||||
@ -13,6 +14,7 @@ Apply(
|
||||
@10-13 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
Space,
|
||||
|
@ -3,6 +3,7 @@ Apply(
|
||||
@1-5 Var {
|
||||
module_name: "",
|
||||
ident: "whee",
|
||||
suffixed: 0,
|
||||
},
|
||||
@0-1 Not,
|
||||
),
|
||||
@ -13,6 +14,7 @@ Apply(
|
||||
@10-13 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
Space,
|
||||
|
@ -2,6 +2,7 @@ Apply(
|
||||
@0-4 Var {
|
||||
module_name: "",
|
||||
ident: "whee",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@5-6 Num(
|
||||
|
@ -17,9 +17,10 @@ SpaceBefore(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@107-108 Identifier(
|
||||
"x",
|
||||
),
|
||||
@107-108 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@111-112 Num(
|
||||
"5",
|
||||
),
|
||||
|
@ -2,6 +2,7 @@ RecordAccess(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "rec",
|
||||
suffixed: 0,
|
||||
},
|
||||
"field",
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "whee",
|
||||
suffixed: 0,
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"a",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@3-4 SpaceBefore(
|
||||
BoundVariable(
|
||||
"c",
|
||||
|
@ -3,6 +3,7 @@ Apply(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -4,6 +4,7 @@ BinOps(
|
||||
@0-1 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@2-4 And,
|
||||
),
|
||||
@ -11,13 +12,15 @@ BinOps(
|
||||
@5-12 Apply(
|
||||
@5-10 Closure(
|
||||
[
|
||||
@6-7 Identifier(
|
||||
"x",
|
||||
),
|
||||
@6-7 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@9-10 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
[
|
||||
|
@ -4,6 +4,7 @@ BinOps(
|
||||
@0-1 Var {
|
||||
module_name: "",
|
||||
ident: "i",
|
||||
suffixed: 0,
|
||||
},
|
||||
@1-2 GreaterThan,
|
||||
),
|
||||
@ -12,13 +13,15 @@ BinOps(
|
||||
@2-7 SpaceAfter(
|
||||
Closure(
|
||||
[
|
||||
@3-4 Identifier(
|
||||
"s",
|
||||
),
|
||||
@3-4 Identifier {
|
||||
ident: "s",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@6-7 Var {
|
||||
module_name: "",
|
||||
ident: "s",
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
[
|
||||
@ -30,6 +33,7 @@ BinOps(
|
||||
@9-10 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@8-9 Negate,
|
||||
),
|
||||
|
@ -30,6 +30,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "q",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
LineComment(
|
||||
|
@ -19,9 +19,10 @@ Defs {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-3 Identifier(
|
||||
"foo",
|
||||
),
|
||||
@0-3 Identifier {
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
@6-7 Num(
|
||||
"1",
|
||||
),
|
||||
|
@ -3,6 +3,7 @@ ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "i",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
LineComment(
|
||||
|
@ -20,9 +20,10 @@ Defs(
|
||||
vars: [
|
||||
@3-4 SpaceAfter(
|
||||
SpaceBefore(
|
||||
Identifier(
|
||||
"h",
|
||||
),
|
||||
Identifier {
|
||||
ident: "h",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
LineComment(
|
||||
"",
|
||||
@ -46,6 +47,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "j",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -17,9 +17,10 @@ Defs(
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 SpaceAfter(
|
||||
Identifier(
|
||||
"w",
|
||||
),
|
||||
Identifier {
|
||||
ident: "w",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
LineComment(
|
||||
"",
|
||||
|
@ -17,9 +17,10 @@ Defs(
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-1 SpaceAfter(
|
||||
Identifier(
|
||||
"t",
|
||||
),
|
||||
Identifier {
|
||||
ident: "t",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
LineComment(
|
||||
"",
|
||||
@ -36,6 +37,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "e",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -98,11 +98,13 @@ Defs(
|
||||
@50-53 Var {
|
||||
module_name: "",
|
||||
ident: "try",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@54-57 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
@59-73 ParensAround(
|
||||
Closure(
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"a",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@2-3 BoundVariable(
|
||||
"b",
|
||||
),
|
||||
@ -28,5 +29,6 @@ Defs(
|
||||
@4-5 Var {
|
||||
module_name: "",
|
||||
ident: "i",
|
||||
suffixed: 0,
|
||||
},
|
||||
)
|
||||
|
@ -21,9 +21,10 @@ Defs(
|
||||
"Email",
|
||||
),
|
||||
[
|
||||
@6-9 Identifier(
|
||||
"str",
|
||||
),
|
||||
@6-9 Identifier {
|
||||
ident: "str",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
@12-36 Apply(
|
||||
@ -46,6 +47,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "str",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -17,9 +17,10 @@ SpaceBefore(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@48-49 Identifier(
|
||||
"x",
|
||||
),
|
||||
@48-49 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@52-53 Num(
|
||||
"5",
|
||||
),
|
||||
|
@ -2,6 +2,7 @@ RecordUpdate {
|
||||
update: @1-2 Var {
|
||||
module_name: "",
|
||||
ident: "e",
|
||||
suffixed: 0,
|
||||
},
|
||||
fields: [],
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ BinOps(
|
||||
@0-1 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@1-3 Equals,
|
||||
),
|
||||
@ -11,5 +12,6 @@ BinOps(
|
||||
@3-4 Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
)
|
||||
|
@ -4,6 +4,7 @@ BinOps(
|
||||
@0-1 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@2-4 Equals,
|
||||
),
|
||||
@ -11,5 +12,6 @@ BinOps(
|
||||
@5-6 Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
)
|
||||
|
@ -33,6 +33,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-5 Identifier(
|
||||
"table",
|
||||
),
|
||||
@0-5 Identifier {
|
||||
ident: "table",
|
||||
suffixed: 0,
|
||||
},
|
||||
@8-44 Function(
|
||||
[
|
||||
@8-35 Record {
|
||||
@ -54,9 +55,10 @@ Defs(
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-5 Identifier(
|
||||
"table",
|
||||
),
|
||||
ann_pattern: @0-5 Identifier {
|
||||
ident: "table",
|
||||
suffixed: 0,
|
||||
},
|
||||
ann_type: @8-44 Function(
|
||||
[
|
||||
@8-35 Record {
|
||||
@ -91,16 +93,18 @@ Defs(
|
||||
),
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @45-50 Identifier(
|
||||
"table",
|
||||
),
|
||||
body_pattern: @45-50 Identifier {
|
||||
ident: "table",
|
||||
suffixed: 0,
|
||||
},
|
||||
body_expr: @53-89 Closure(
|
||||
[
|
||||
@54-62 RecordDestructure(
|
||||
[
|
||||
@55-61 Identifier(
|
||||
"height",
|
||||
),
|
||||
@55-61 Identifier {
|
||||
ident: "height",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -123,6 +127,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "table",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
@4-20 Function(
|
||||
[
|
||||
@4-10 Tuple {
|
||||
@ -53,9 +54,10 @@ Defs(
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
ann_pattern: @0-1 Identifier {
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
ann_type: @4-20 Function(
|
||||
[
|
||||
@4-10 Tuple {
|
||||
@ -89,18 +91,21 @@ Defs(
|
||||
},
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @21-22 Identifier(
|
||||
"f",
|
||||
),
|
||||
body_pattern: @21-22 Identifier {
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
body_expr: @25-32 Closure(
|
||||
[
|
||||
@26-27 Identifier(
|
||||
"x",
|
||||
),
|
||||
@26-27 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@31-32 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
},
|
||||
@ -111,6 +116,7 @@ Defs(
|
||||
@34-35 Var {
|
||||
module_name: "",
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@36-47 Tuple(
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
@4-21 Function(
|
||||
[
|
||||
@4-7 Apply(
|
||||
@ -45,9 +46,10 @@ Defs(
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
ann_pattern: @0-1 Identifier {
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
ann_type: @4-21 Function(
|
||||
[
|
||||
@4-7 Apply(
|
||||
@ -73,20 +75,23 @@ Defs(
|
||||
},
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @22-23 Identifier(
|
||||
"f",
|
||||
),
|
||||
body_pattern: @22-23 Identifier {
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
body_expr: @26-42 Closure(
|
||||
[
|
||||
@27-28 Identifier(
|
||||
"x",
|
||||
),
|
||||
@27-28 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@32-42 Tuple(
|
||||
[
|
||||
@33-34 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@36-41 BinOps(
|
||||
[
|
||||
@ -94,6 +99,7 @@ Defs(
|
||||
@36-37 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@38-39 Plus,
|
||||
),
|
||||
@ -113,6 +119,7 @@ Defs(
|
||||
@44-45 Var {
|
||||
module_name: "",
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@46-48 Num(
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier(
|
||||
"iffy",
|
||||
),
|
||||
@0-4 Identifier {
|
||||
ident: "iffy",
|
||||
suffixed: 0,
|
||||
},
|
||||
@5-6 Num(
|
||||
"5",
|
||||
),
|
||||
|
@ -18,6 +18,7 @@ BinOps(
|
||||
Var {
|
||||
module_name: "Str",
|
||||
ident: "toUtf8",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
@ -31,13 +32,15 @@ BinOps(
|
||||
@28-36 Var {
|
||||
module_name: "List",
|
||||
ident: "map",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@37-54 Closure(
|
||||
[
|
||||
@38-42 Identifier(
|
||||
"byte",
|
||||
),
|
||||
@38-42 Identifier {
|
||||
ident: "byte",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@46-54 BinOps(
|
||||
[
|
||||
@ -45,6 +48,7 @@ BinOps(
|
||||
@46-50 Var {
|
||||
module_name: "",
|
||||
ident: "byte",
|
||||
suffixed: 0,
|
||||
},
|
||||
@51-52 Plus,
|
||||
),
|
||||
@ -67,5 +71,6 @@ BinOps(
|
||||
@58-70 Var {
|
||||
module_name: "List",
|
||||
ident: "reverse",
|
||||
suffixed: 0,
|
||||
},
|
||||
)
|
||||
|
@ -1,8 +1,9 @@
|
||||
Closure(
|
||||
[
|
||||
@1-2 Identifier(
|
||||
"x",
|
||||
),
|
||||
@1-2 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@8-9 SpaceBefore(
|
||||
Num(
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-6 Identifier(
|
||||
"myList",
|
||||
),
|
||||
@0-6 Identifier {
|
||||
ident: "myList",
|
||||
suffixed: 0,
|
||||
},
|
||||
@9-57 List(
|
||||
Collection {
|
||||
items: [
|
||||
@ -38,6 +39,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
@ -47,6 +49,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-6 Identifier(
|
||||
"myList",
|
||||
),
|
||||
@0-6 Identifier {
|
||||
ident: "myList",
|
||||
suffixed: 0,
|
||||
},
|
||||
@9-25 List(
|
||||
[
|
||||
@15-16 SpaceBefore(
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-6 Identifier(
|
||||
"myList",
|
||||
),
|
||||
@0-6 Identifier {
|
||||
ident: "myList",
|
||||
suffixed: 0,
|
||||
},
|
||||
@9-26 List(
|
||||
Collection {
|
||||
items: [
|
||||
|
@ -19,5 +19,6 @@ BinOps(
|
||||
@6-7 Var {
|
||||
module_name: "",
|
||||
ident: "i",
|
||||
suffixed: 0,
|
||||
},
|
||||
)
|
||||
|
@ -73,18 +73,22 @@ When(
|
||||
@60-72 SpaceBefore(
|
||||
List(
|
||||
[
|
||||
@61-62 Identifier(
|
||||
"a",
|
||||
),
|
||||
@64-65 Identifier(
|
||||
"b",
|
||||
),
|
||||
@67-68 Identifier(
|
||||
"c",
|
||||
),
|
||||
@70-71 Identifier(
|
||||
"d",
|
||||
),
|
||||
@61-62 Identifier {
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@64-65 Identifier {
|
||||
ident: "b",
|
||||
suffixed: 0,
|
||||
},
|
||||
@67-68 Identifier {
|
||||
ident: "c",
|
||||
suffixed: 0,
|
||||
},
|
||||
@70-71 Identifier {
|
||||
ident: "d",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
[
|
||||
@ -102,12 +106,14 @@ When(
|
||||
@81-91 SpaceBefore(
|
||||
List(
|
||||
[
|
||||
@82-83 Identifier(
|
||||
"a",
|
||||
),
|
||||
@85-86 Identifier(
|
||||
"b",
|
||||
),
|
||||
@82-83 Identifier {
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@85-86 Identifier {
|
||||
ident: "b",
|
||||
suffixed: 0,
|
||||
},
|
||||
@88-90 ListRest(
|
||||
None,
|
||||
),
|
||||
@ -131,12 +137,14 @@ When(
|
||||
@101-103 ListRest(
|
||||
None,
|
||||
),
|
||||
@105-106 Identifier(
|
||||
"c",
|
||||
),
|
||||
@108-109 Identifier(
|
||||
"d",
|
||||
),
|
||||
@105-106 Identifier {
|
||||
ident: "c",
|
||||
suffixed: 0,
|
||||
},
|
||||
@108-109 Identifier {
|
||||
ident: "d",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
[
|
||||
@ -170,9 +178,10 @@ When(
|
||||
),
|
||||
@131-134 List(
|
||||
[
|
||||
@132-133 Identifier(
|
||||
"a",
|
||||
),
|
||||
@132-133 Identifier {
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -207,9 +216,10 @@ When(
|
||||
@156-158 List(
|
||||
[],
|
||||
),
|
||||
@160-161 Identifier(
|
||||
"x",
|
||||
),
|
||||
@160-161 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -17,9 +17,10 @@ SpaceBefore(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@113-114 Identifier(
|
||||
"x",
|
||||
),
|
||||
@113-114 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@117-118 Num(
|
||||
"5",
|
||||
),
|
||||
|
@ -15,9 +15,10 @@ Defs {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier(
|
||||
"main",
|
||||
),
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
suffixed: 0,
|
||||
},
|
||||
@11-24 SpaceBefore(
|
||||
Defs(
|
||||
Defs {
|
||||
@ -37,9 +38,10 @@ Defs {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier(
|
||||
"i",
|
||||
),
|
||||
@11-12 Identifier {
|
||||
ident: "i",
|
||||
suffixed: 0,
|
||||
},
|
||||
@15-17 Num(
|
||||
"64",
|
||||
),
|
||||
@ -50,6 +52,7 @@ Defs {
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "i",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -1,16 +1,19 @@
|
||||
Backpassing(
|
||||
[
|
||||
@0-1 Identifier(
|
||||
"x",
|
||||
),
|
||||
@3-4 Identifier(
|
||||
"y",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@3-4 Identifier {
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@8-23 Apply(
|
||||
@8-17 Var {
|
||||
module_name: "List",
|
||||
ident: "map2",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@18-20 List(
|
||||
@ -29,6 +32,7 @@ Backpassing(
|
||||
@25-26 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@27-28 Plus,
|
||||
),
|
||||
@ -36,6 +40,7 @@ Backpassing(
|
||||
@29-30 Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
[
|
||||
|
@ -15,23 +15,27 @@ Defs {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier(
|
||||
"main",
|
||||
),
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
suffixed: 0,
|
||||
},
|
||||
@12-50 SpaceBefore(
|
||||
Backpassing(
|
||||
[
|
||||
@12-16 Identifier(
|
||||
"arg1",
|
||||
),
|
||||
@18-22 Identifier(
|
||||
"arg2",
|
||||
),
|
||||
@12-16 Identifier {
|
||||
ident: "arg1",
|
||||
suffixed: 0,
|
||||
},
|
||||
@18-22 Identifier {
|
||||
ident: "arg2",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@26-30 Apply(
|
||||
@26-27 Var {
|
||||
module_name: "",
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@28-30 Record(
|
||||
|
@ -10,13 +10,15 @@ Backpassing(
|
||||
),
|
||||
],
|
||||
),
|
||||
@5-6 Identifier(
|
||||
"r",
|
||||
),
|
||||
@5-6 Identifier {
|
||||
ident: "r",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@10-11 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@12-13 SpaceBefore(
|
||||
Tag(
|
||||
|
@ -27,9 +27,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-1 Identifier(
|
||||
"a",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@4-22 Str(
|
||||
Line(
|
||||
[
|
||||
@ -50,9 +51,10 @@ Defs(
|
||||
),
|
||||
),
|
||||
Body(
|
||||
@23-24 Identifier(
|
||||
"b",
|
||||
),
|
||||
@23-24 Identifier {
|
||||
ident: "b",
|
||||
suffixed: 0,
|
||||
},
|
||||
@27-49 Str(
|
||||
Block(
|
||||
[
|
||||
@ -75,9 +77,10 @@ Defs(
|
||||
),
|
||||
),
|
||||
Body(
|
||||
@50-51 Identifier(
|
||||
"c",
|
||||
),
|
||||
@50-51 Identifier {
|
||||
ident: "c",
|
||||
suffixed: 0,
|
||||
},
|
||||
@58-92 SpaceBefore(
|
||||
Str(
|
||||
Block(
|
||||
|
@ -2,6 +2,7 @@ Apply(
|
||||
@0-1 Var {
|
||||
module_name: "",
|
||||
ident: "e",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@1-10 Str(
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
@8-10 SpaceBefore(
|
||||
Record {
|
||||
fields: [],
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@0-1 Identifier(
|
||||
"f",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "f",
|
||||
suffixed: 0,
|
||||
},
|
||||
@17-19 SpaceBefore(
|
||||
Record {
|
||||
fields: [],
|
||||
|
@ -4,6 +4,7 @@ RecordAccess(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "rec",
|
||||
suffixed: 0,
|
||||
},
|
||||
"abc",
|
||||
),
|
||||
|
@ -2,6 +2,7 @@ UnaryOp(
|
||||
@1-4 Var {
|
||||
module_name: "",
|
||||
ident: "inf",
|
||||
suffixed: 0,
|
||||
},
|
||||
@0-1 Negate,
|
||||
)
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-1 Identifier(
|
||||
"a",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@2-9 Apply(
|
||||
@2-3 SpaceAfter(
|
||||
Tag(
|
||||
@ -33,12 +34,14 @@ Defs(
|
||||
@6-7 Var {
|
||||
module_name: "",
|
||||
ident: "g",
|
||||
suffixed: 0,
|
||||
},
|
||||
@5-6 Negate,
|
||||
),
|
||||
@8-9 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
Space,
|
||||
@ -50,6 +53,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier(
|
||||
"main",
|
||||
),
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
suffixed: 0,
|
||||
},
|
||||
@11-71 SpaceBefore(
|
||||
Defs(
|
||||
Defs {
|
||||
@ -38,19 +39,22 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-15 Identifier(
|
||||
"task",
|
||||
),
|
||||
@11-15 Identifier {
|
||||
ident: "task",
|
||||
suffixed: 0,
|
||||
},
|
||||
@18-62 Backpassing(
|
||||
[
|
||||
@18-22 Identifier(
|
||||
"file",
|
||||
),
|
||||
@18-22 Identifier {
|
||||
ident: "file",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@43-46 SpaceBefore(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
@ -60,6 +64,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
@ -73,6 +78,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "task",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -15,9 +15,10 @@ Defs {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier(
|
||||
"main",
|
||||
),
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
suffixed: 0,
|
||||
},
|
||||
@11-115 SpaceBefore(
|
||||
Defs(
|
||||
Defs {
|
||||
@ -37,9 +38,10 @@ Defs {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@11-23 Identifier(
|
||||
"wrappedNotEq",
|
||||
),
|
||||
@11-23 Identifier {
|
||||
ident: "wrappedNotEq",
|
||||
suffixed: 0,
|
||||
},
|
||||
@26-38 Function(
|
||||
[
|
||||
@26-27 BoundVariable(
|
||||
@ -57,9 +59,10 @@ Defs {
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @11-23 Identifier(
|
||||
"wrappedNotEq",
|
||||
),
|
||||
ann_pattern: @11-23 Identifier {
|
||||
ident: "wrappedNotEq",
|
||||
suffixed: 0,
|
||||
},
|
||||
ann_type: @26-38 Function(
|
||||
[
|
||||
@26-27 BoundVariable(
|
||||
@ -76,17 +79,20 @@ Defs {
|
||||
),
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @43-55 Identifier(
|
||||
"wrappedNotEq",
|
||||
),
|
||||
body_pattern: @43-55 Identifier {
|
||||
ident: "wrappedNotEq",
|
||||
suffixed: 0,
|
||||
},
|
||||
body_expr: @58-93 Closure(
|
||||
[
|
||||
@59-63 Identifier(
|
||||
"num1",
|
||||
),
|
||||
@65-69 Identifier(
|
||||
"num2",
|
||||
),
|
||||
@59-63 Identifier {
|
||||
ident: "num1",
|
||||
suffixed: 0,
|
||||
},
|
||||
@65-69 Identifier {
|
||||
ident: "num2",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@81-93 SpaceBefore(
|
||||
BinOps(
|
||||
@ -95,6 +101,7 @@ Defs {
|
||||
@81-85 Var {
|
||||
module_name: "",
|
||||
ident: "num1",
|
||||
suffixed: 0,
|
||||
},
|
||||
@86-88 NotEquals,
|
||||
),
|
||||
@ -102,6 +109,7 @@ Defs {
|
||||
@89-93 Var {
|
||||
module_name: "",
|
||||
ident: "num2",
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
[
|
||||
@ -117,6 +125,7 @@ Defs {
|
||||
@99-111 Var {
|
||||
module_name: "",
|
||||
ident: "wrappedNotEq",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@112-113 Num(
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-1 Identifier(
|
||||
"x",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@2-7 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
@ -37,9 +38,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Annotation(
|
||||
@2-3 Identifier(
|
||||
"a",
|
||||
),
|
||||
@2-3 Identifier {
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
@4-5 BoundVariable(
|
||||
"n",
|
||||
),
|
||||
|
@ -4,6 +4,7 @@ If(
|
||||
@3-5 Var {
|
||||
module_name: "",
|
||||
ident: "t1",
|
||||
suffixed: 0,
|
||||
},
|
||||
@13-14 SpaceBefore(
|
||||
SpaceAfter(
|
||||
@ -23,6 +24,7 @@ If(
|
||||
@23-25 Var {
|
||||
module_name: "",
|
||||
ident: "t2",
|
||||
suffixed: 0,
|
||||
},
|
||||
@33-34 SpaceBefore(
|
||||
SpaceAfter(
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-1 Identifier(
|
||||
"x",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@8-9 SpaceBefore(
|
||||
Num(
|
||||
"5",
|
||||
|
@ -16,9 +16,10 @@ Defs(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-1 Identifier(
|
||||
"x",
|
||||
),
|
||||
@0-1 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@4-13 BinOps(
|
||||
[
|
||||
(
|
||||
|
@ -100,14 +100,16 @@ Full {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@199-203 Identifier(
|
||||
"main",
|
||||
),
|
||||
@199-203 Identifier {
|
||||
ident: "main",
|
||||
suffixed: 0,
|
||||
},
|
||||
@210-246 SpaceBefore(
|
||||
Apply(
|
||||
@210-221 Var {
|
||||
module_name: "Stdout",
|
||||
ident: "line",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@222-246 Str(
|
||||
|
@ -43,6 +43,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "p",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -32,6 +32,7 @@ Defs(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -1,20 +1,23 @@
|
||||
SpaceBefore(
|
||||
Backpassing(
|
||||
[
|
||||
@18-19 Identifier(
|
||||
"x",
|
||||
),
|
||||
@18-19 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@23-32 ParensAround(
|
||||
Closure(
|
||||
[
|
||||
@25-26 Identifier(
|
||||
"y",
|
||||
),
|
||||
@25-26 Identifier {
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
@30-31 Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -22,6 +25,7 @@ SpaceBefore(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
|
@ -17,9 +17,10 @@ SpaceBefore(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@18-19 Identifier(
|
||||
"x",
|
||||
),
|
||||
@18-19 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@20-21 Num(
|
||||
"5",
|
||||
),
|
||||
|
@ -17,9 +17,10 @@ SpaceBefore(
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@18-19 Identifier(
|
||||
"x",
|
||||
),
|
||||
@18-19 Identifier {
|
||||
ident: "x",
|
||||
suffixed: 0,
|
||||
},
|
||||
@22-23 Num(
|
||||
"5",
|
||||
),
|
||||
|
@ -21,15 +21,17 @@ Defs(
|
||||
"@Thunk",
|
||||
),
|
||||
[
|
||||
@7-9 Identifier(
|
||||
"it",
|
||||
),
|
||||
@7-9 Identifier {
|
||||
ident: "it",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
),
|
||||
@12-22 Apply(
|
||||
@12-14 Var {
|
||||
module_name: "",
|
||||
ident: "id",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@16-21 ParensAround(
|
||||
@ -56,6 +58,7 @@ Defs(
|
||||
@23-25 Var {
|
||||
module_name: "",
|
||||
ident: "it",
|
||||
suffixed: 0,
|
||||
},
|
||||
[
|
||||
@26-28 Record(
|
||||
|
@ -6,10 +6,12 @@ Apply(
|
||||
@5-6 Var {
|
||||
module_name: "",
|
||||
ident: "m",
|
||||
suffixed: 0,
|
||||
},
|
||||
@7-8 Var {
|
||||
module_name: "",
|
||||
ident: "n",
|
||||
suffixed: 0,
|
||||
},
|
||||
],
|
||||
Space,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user