Merge pull request #6634 from roc-lang/suffixed-bang

De-sugar bang `!` operator
This commit is contained in:
Richard Feldman 2024-04-15 20:38:55 -04:00 committed by GitHub
commit c74685f74a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
198 changed files with 4417 additions and 1170 deletions

2
Cargo.lock generated
View File

@ -2329,6 +2329,7 @@ dependencies = [
"roc_problem", "roc_problem",
"roc_region", "roc_region",
"roc_serialize", "roc_serialize",
"roc_test_utils",
"roc_types", "roc_types",
"static_assertions", "static_assertions",
"ven_pretty", "ven_pretty",
@ -2531,6 +2532,7 @@ version = "0.0.1"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"roc_collections", "roc_collections",
"roc_error_macros",
"roc_module", "roc_module",
"roc_parse", "roc_parse",
"roc_region", "roc_region",

View File

@ -17,6 +17,7 @@ roc_problem = { path = "../problem" }
roc_region = { path = "../region" } roc_region = { path = "../region" }
roc_serialize = { path = "../serialize" } roc_serialize = { path = "../serialize" }
roc_types = { path = "../types" } roc_types = { path = "../types" }
roc_test_utils = { path = "../../test_utils" }
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }

View File

@ -748,9 +748,10 @@ fn can_annotation_help(
for loc_var in *loc_vars { for loc_var in *loc_vars {
let var = match loc_var.value { let var = match loc_var.value {
Pattern::Identifier(name) if name.chars().next().unwrap().is_lowercase() => { Pattern::Identifier {
name ident: name,
} suffixed: _,
} if name.chars().next().unwrap().is_lowercase() => name,
_ => unreachable!("I thought this was validated during parsing"), _ => unreachable!("I thought this was validated during parsing"),
}; };
let var_name = Lowercase::from(var); let var_name = Lowercase::from(var);

View File

@ -547,7 +547,11 @@ fn canonicalize_claimed_ability_impl<'a>(
} }
AssignedField::RequiredValue(label, _spaces, value) => { AssignedField::RequiredValue(label, _spaces, value) => {
let impl_ident = match value.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() { if module_name.is_empty() {
ident ident
} else { } else {
@ -2570,9 +2574,10 @@ fn to_pending_alias_or_opaque<'a>(
for loc_var in vars.iter() { for loc_var in vars.iter() {
match loc_var.value { match loc_var.value {
ast::Pattern::Identifier(name) ast::Pattern::Identifier {
if name.chars().next().unwrap().is_lowercase() => ident: name,
{ suffixed: _,
} if name.chars().next().unwrap().is_lowercase() => {
let lowercase = Lowercase::from(name); let lowercase = Lowercase::from(name);
can_rigids.push(Loc { can_rigids.push(Loc {
value: lowercase, value: lowercase,
@ -2874,6 +2879,8 @@ fn to_pending_value_def<'a>(
condition, condition,
preceding_comment: *preceding_comment, preceding_comment: *preceding_comment,
}), }),
Stmt(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"),
} }
} }

View File

@ -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_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
let opaque_apply_pattern = ast::Pattern::Apply( let opaque_apply_pattern = ast::Pattern::Apply(
opaque_ref, opaque_ref,
&*env &*env.arena.alloc([Loc::at(
.arena DERIVED_REGION,
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]), ast::Pattern::Identifier {
ident: payload,
suffixed: 0,
},
)]),
); );
// Encode.toEncoder payload // 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 { alloc_expr(ast::Expr::Var {
module_name: "Encode", module_name: "Encode",
ident: "toEncoder", ident: "toEncoder",
suffixed: 0,
}), }),
&*env.arena.alloc([&*alloc_expr(ast::Expr::Var { &*env.arena.alloc([&*alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: payload, ident: payload,
suffixed: 0,
})]), })]),
roc_module::called_via::CalledVia::Space, 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 { alloc_expr(ast::Expr::Var {
module_name: "Decode", module_name: "Decode",
ident: "decodeWith", ident: "decodeWith",
suffixed: 0,
}), }),
env.arena.alloc([ env.arena.alloc([
&*alloc_expr(ast::Expr::Var { &*alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: bytes, ident: bytes,
suffixed: 0,
}), }),
alloc_expr(ast::Expr::Var { alloc_expr(ast::Expr::Var {
module_name: "Decode", module_name: "Decode",
ident: "decoder", ident: "decoder",
suffixed: 0,
}), }),
alloc_expr(ast::Expr::Var { alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: fmt, ident: fmt,
suffixed: 0,
}), }),
]), ]),
CalledVia::Space, 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 { alloc_expr(ast::Expr::Var {
module_name: "Decode", module_name: "Decode",
ident: "mapResult", ident: "mapResult",
suffixed: 0,
}), }),
env.arena.alloc([ env.arena.alloc([
&*alloc_expr(call_decode_with), &*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 // Decode.mapResult (Decode.decodeWith bytes Decode.decoder fmt) @Opaq
let custom_closure = ast::Expr::Closure( let custom_closure = ast::Expr::Closure(
env.arena.alloc([ env.arena.alloc([
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(bytes)), Loc::at(
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(fmt)), 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), 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 { alloc_expr(ast::Expr::Var {
module_name: "Decode", module_name: "Decode",
ident: "custom", ident: "custom",
suffixed: 0,
}), }),
env.arena.alloc([&*alloc_expr(custom_closure)]), env.arena.alloc([&*alloc_expr(custom_closure)]),
CalledVia::Space, 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_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
let opaque_apply_pattern = ast::Pattern::Apply( let opaque_apply_pattern = ast::Pattern::Apply(
opaque_ref, opaque_ref,
&*env &*env.arena.alloc([Loc::at(
.arena DERIVED_REGION,
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]), ast::Pattern::Identifier {
ident: payload,
suffixed: 0,
},
)]),
); );
// Hash.hash hasher payload // 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 { alloc_expr(ast::Expr::Var {
module_name: "Hash", module_name: "Hash",
ident: "hash", ident: "hash",
suffixed: 0,
}), }),
&*env.arena.alloc([ &*env.arena.alloc([
&*alloc_expr(ast::Expr::Var { &*alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: hasher, ident: hasher,
suffixed: 0,
}), }),
&*alloc_expr(ast::Expr::Var { &*alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: payload, ident: payload,
suffixed: 0,
}), }),
]), ]),
roc_module::called_via::CalledVia::Space, 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 // \hasher, @Opaq payload -> Hash.hash hasher payload
ast::Expr::Closure( ast::Expr::Closure(
env.arena.alloc([ 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), Loc::at(DERIVED_REGION, opaque_apply_pattern),
]), ]),
call_member, call_member,
@ -172,16 +209,24 @@ fn is_eq<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
// \@Opaq payload1 // \@Opaq payload1
let opaque1 = ast::Pattern::Apply( let opaque1 = ast::Pattern::Apply(
opaque_ref, opaque_ref,
&*env &*env.arena.alloc([Loc::at(
.arena DERIVED_REGION,
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload1))]), ast::Pattern::Identifier {
ident: payload1,
suffixed: 0,
},
)]),
); );
// \@Opaq payload2 // \@Opaq payload2
let opaque2 = ast::Pattern::Apply( let opaque2 = ast::Pattern::Apply(
opaque_ref, opaque_ref,
&*env &*env.arena.alloc([Loc::at(
.arena DERIVED_REGION,
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload2))]), ast::Pattern::Identifier {
ident: payload2,
suffixed: 0,
},
)]),
); );
// Bool.isEq payload1 payload2 // 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 { alloc_expr(ast::Expr::Var {
module_name: "Bool", module_name: "Bool",
ident: "isEq", ident: "isEq",
suffixed: 0,
}), }),
&*env.arena.alloc([ &*env.arena.alloc([
&*alloc_expr(ast::Expr::Var { &*alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: payload1, ident: payload1,
suffixed: 0,
}), }),
&*alloc_expr(ast::Expr::Var { &*alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: payload2, ident: payload2,
suffixed: 0,
}), }),
]), ]),
roc_module::called_via::CalledVia::Space, 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_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
let opaque_apply_pattern = ast::Pattern::Apply( let opaque_apply_pattern = ast::Pattern::Apply(
opaque_ref, opaque_ref,
&*env &*env.arena.alloc([Loc::at(
.arena DERIVED_REGION,
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]), ast::Pattern::Identifier {
ident: payload,
suffixed: 0,
},
)]),
); );
// Inspect.toInspector payload // 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 { alloc_expr(ast::Expr::Var {
module_name: "Inspect", module_name: "Inspect",
ident: "toInspector", ident: "toInspector",
suffixed: 0,
}), }),
&*env.arena.alloc([&*alloc_expr(ast::Expr::Var { &*env.arena.alloc([&*alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: payload, ident: payload,
suffixed: 0,
})]), })]),
roc_module::called_via::CalledVia::Space, 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 { alloc_expr(ast::Expr::Var {
module_name: "Inspect", module_name: "Inspect",
ident: "tag", ident: "tag",
suffixed: 0,
}), }),
&*env.arena.alloc([&*opaque_name, &*to_inspector_list]), &*env.arena.alloc([&*opaque_name, &*to_inspector_list]),
roc_module::called_via::CalledVia::Space, 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 { alloc_expr(ast::Expr::Var {
module_name: "Inspect", module_name: "Inspect",
ident: "apply", ident: "apply",
suffixed: 0,
}), }),
&*env.arena.alloc([ &*env.arena.alloc([
&*opaque_inspector, &*opaque_inspector,
&*alloc_expr(ast::Expr::Var { &*alloc_expr(ast::Expr::Var {
module_name: "", module_name: "",
ident: fmt, ident: fmt,
suffixed: 0,
}), }),
]), ]),
roc_module::called_via::CalledVia::Space, roc_module::called_via::CalledVia::Space,
)); ));
let custom_closure = alloc_expr(ast::Expr::Closure( let custom_closure = alloc_expr(ast::Expr::Closure(
env.arena env.arena.alloc([Loc::at(
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(fmt))]), DERIVED_REGION,
ast::Pattern::Identifier {
ident: fmt,
suffixed: 0,
},
)]),
apply_opaque_inspector, 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 { alloc_expr(ast::Expr::Var {
module_name: "Inspect", module_name: "Inspect",
ident: "custom", ident: "custom",
suffixed: 0,
}), }),
env.arena.alloc([&*custom_closure]), env.arena.alloc([&*custom_closure]),
CalledVia::Space, CalledVia::Space,

View File

@ -1,5 +1,6 @@
#![allow(clippy::manual_map)] #![allow(clippy::manual_map)]
use crate::suffixed::{apply_task_await, unwrap_suffixed_expression, EUnwrapped};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
@ -56,7 +57,11 @@ fn new_op_call_expr<'a>(
let args = arena.alloc([left, right]); let args = arena.alloc([left, right]);
let loc_expr = arena.alloc(Loc { let loc_expr = arena.alloc(Loc {
value: Expr::Var { module_name, ident }, value: Expr::Var {
module_name,
ident,
suffixed: 0,
},
region: loc_op.region, region: loc_op.region,
}); });
@ -92,9 +97,10 @@ fn desugar_value_def<'a>(
ann_pattern, ann_pattern,
ann_type, ann_type,
comment: *comment, 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), body_expr: desugar_expr(arena, body_expr, src, line_info, module_path),
}, },
Dbg { Dbg {
condition, condition,
preceding_comment, preceding_comment,
@ -128,6 +134,18 @@ fn desugar_value_def<'a>(
preceding_comment: *preceding_comment, 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, src: &'a str,
line_info: &mut Option<LineInfo>, line_info: &mut Option<LineInfo>,
module_path: &str, module_path: &str,
top_level_def: bool,
) { ) {
for value_def in defs.value_defs.iter_mut() { for value_def in defs.value_defs.iter_mut() {
*value_def = desugar_value_def(arena, arena.alloc(*value_def), src, line_info, module_path); *value_def = desugar_value_def(arena, arena.alloc(*value_def), src, line_info, module_path);
} }
}
fn desugar_defs_node_suffixed<'a>( // `desugar_defs_node_values` is called recursively in `desugar_expr` and we
arena: &'a Bump, // only we only want to unwrap suffixed nodes if they are a top level def.
loc_expr: &'a Loc<Expr<'a>>, //
) -> &'a Loc<Expr<'a>> { // check here first so we only unwrap the expressions once, and after they have
match loc_expr.value { // been desugared
Defs(defs, loc_ret) => { if top_level_def {
match defs.search_suffixed_defs() { for value_def in defs.value_defs.iter_mut() {
None => loc_expr, *value_def = desugar_value_def_suffixed(arena, *value_def);
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),
))
}
}
}
} }
_ => 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 /// For each top-level ValueDef in our module, we will unwrap any suffixed
fn unwrap_suffixed_def_and_pattern<'a>( /// expressions
arena: &'a Bump, ///
region: Region, /// e.g. `say! "hi"` desugars to `Task.await (say "hi") -> \{} -> ...`
value_def: ValueDef<'a>, pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) -> ValueDef<'a> {
) -> ( use ValueDef::*;
roc_parse::ast::Expr<'a>,
&'a Loc<roc_parse::ast::Pattern<'a>>,
) {
match value_def { match value_def {
ValueDef::Body(pattern, suffixed_expression) => match suffixed_expression.value { Body(loc_pattern, loc_expr) => {
// The Suffixed has arguments applied e.g. `Stdout.line! "Hello World"` // note called_from_def is passed as `false` as this is a top_level_def
Apply(sub_loc, suffixed_args, called_via) => match sub_loc.value { match unwrap_suffixed_expression(arena, loc_expr, None) {
Suffixed(sub_expr) => ( Ok(new_expr) => Body(loc_pattern, new_expr),
Apply( Err(EUnwrapped::UnwrappedSubExpr {
arena.alloc(Loc::at(region, *sub_expr)), sub_arg,
suffixed_args, sub_pat,
called_via, sub_new,
), }) => {
pattern, 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 { .. } | Underscore { .. }
| MalformedIdent(_, _) | MalformedIdent(_, _)
| MalformedClosure | MalformedClosure
| MalformedSuffixed(..)
| PrecedenceConflict { .. } | PrecedenceConflict { .. }
| MultipleRecordBuilders { .. } | MultipleRecordBuilders { .. }
| UnappliedRecordBuilder { .. } | UnappliedRecordBuilder { .. }
@ -581,14 +492,10 @@ pub fn desugar_expr<'a>(
), ),
Defs(defs, loc_ret) => { Defs(defs, loc_ret) => {
let mut defs = (*defs).clone(); 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); let loc_ret = desugar_expr(arena, loc_ret, src, line_info, module_path);
// Desugar any Suffixed nodes arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret)))
desugar_defs_node_suffixed(
arena,
arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret))),
)
} }
Apply(loc_fn, loc_args, called_via) => { Apply(loc_fn, loc_args, called_via) => {
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena); let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
@ -699,10 +606,12 @@ pub fn desugar_expr<'a>(
Negate => Var { Negate => Var {
module_name: ModuleName::NUM, module_name: ModuleName::NUM,
ident: "neg", ident: "neg",
suffixed: 0,
}, },
Not => Var { Not => Var {
module_name: ModuleName::BOOL, module_name: ModuleName::BOOL,
ident: "not", ident: "not",
suffixed: 0,
}, },
}; };
let loc_fn_var = arena.alloc(Loc { region, value }); let loc_fn_var = arena.alloc(Loc { region, value });
@ -800,6 +709,7 @@ pub fn desugar_expr<'a>(
let inspect_fn = Var { let inspect_fn = Var {
module_name: ModuleName::INSPECT, module_name: ModuleName::INSPECT,
ident: "toStr", ident: "toStr",
suffixed: 0,
}; };
let loc_inspect_fn_var = arena.alloc(Loc { let loc_inspect_fn_var = arena.alloc(Loc {
value: inspect_fn, value: inspect_fn,
@ -839,31 +749,32 @@ pub fn desugar_expr<'a>(
region: loc_expr.region, region: loc_expr.region,
}) })
} }
LowLevelDbg(_, _, _) => unreachable!("Only exists after desugaring"),
Suffixed(expr) => { // Replace an empty final def with a `Task.ok {}`
// Rewrite `Suffixed(BinOps([args...], Var(...)))` to `BinOps([args...], Suffixed(Var(...)))` EmptyDefsFinal => {
// This is to handle cases like e.g. `"Hello" |> line!` let mut apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
if let BinOps(args, sub_expr) = expr { apply_args
return desugar_expr( .push(arena.alloc(Loc::at(loc_expr.region, Expr::Record(Collection::empty()))));
arena,
arena.alloc(Loc::at(
loc_expr.region,
Expr::Apply(
arena.alloc(Loc::at( arena.alloc(Loc::at(
loc_expr.region, loc_expr.region,
BinOps( Expr::Var {
args, module_name: ModuleName::TASK,
arena.alloc(Loc::at(sub_expr.region, Suffixed(&sub_expr.value))), ident: "ok",
), suffixed: 0,
},
)), )),
src, arena.alloc(apply_args),
line_info, CalledVia::BangSuffix,
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
} }
// note this only exists after desugaring
LowLevelDbg(_, _, _) => loc_expr,
} }
} }
@ -949,6 +860,7 @@ fn desugar_field<'a>(
value: Var { value: Var {
module_name: "", module_name: "",
ident: loc_str.value, ident: loc_str.value,
suffixed: 0,
}, },
region: loc_str.region, region: loc_str.region,
}; };
@ -1009,7 +921,7 @@ fn desugar_pattern<'a>(
use roc_parse::ast::Pattern::*; use roc_parse::ast::Pattern::*;
match pattern { match pattern {
Identifier(_) Identifier { .. }
| Tag(_) | Tag(_)
| OpaqueRef(_) | OpaqueRef(_)
| NumLiteral(_) | NumLiteral(_)
@ -1129,6 +1041,7 @@ fn record_builder_arg<'a>(
value: Expr::Var { value: Expr::Var {
module_name: "", module_name: "",
ident: arena.alloc("#".to_owned() + label.value), 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() { for label in apply_field_names.iter().rev() {
let name = arena.alloc("#".to_owned() + label.value); 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 { let arg_pattern = arena.alloc(Loc {
value: ident, value: ident,

View File

@ -623,6 +623,9 @@ pub fn canonicalize_expr<'a>(
use Expr::*; use Expr::*;
let (expr, output) = match expr { let (expr, output) = match expr {
&ast::Expr::EmptyDefsFinal => {
internal_error!("EmptyDefsFinal should have been desugared")
}
&ast::Expr::Num(str) => { &ast::Expr::Num(str) => {
let answer = num_expr_from_result(var_store, finish_parsing_num(str), region, env); 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) (expr, output)
} }
} }
ast::Expr::Var { module_name, ident } => { ast::Expr::Var {
canonicalize_var_lookup(env, var_store, scope, module_name, ident, region) 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) => { ast::Expr::Underscore(name) => {
// we parse underscores, but they are not valid expression syntax // we parse underscores, but they are not valid expression syntax
@ -1375,6 +1380,10 @@ pub fn canonicalize_expr<'a>(
(RuntimeError(problem), Output::default()) (RuntimeError(problem), Output::default())
} }
ast::Expr::MalformedSuffixed(..) => {
use roc_problem::can::RuntimeError::*;
(RuntimeError(MalformedSuffixed(region)), Output::default())
}
ast::Expr::MultipleRecordBuilders(sub_expr) => { ast::Expr::MultipleRecordBuilders(sub_expr) => {
use roc_problem::can::RuntimeError::*; use roc_problem::can::RuntimeError::*;
@ -1441,12 +1450,6 @@ pub fn canonicalize_expr<'a>(
bad_expr 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. // 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::IngestedFile(_, _)
| ast::Expr::SpaceBefore(_, _) | ast::Expr::SpaceBefore(_, _)
| ast::Expr::Str(StrLiteral::Block(_)) | 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 // These can contain subexpressions, so we need to recursively check those
ast::Expr::Str(StrLiteral::Line(segments)) => { ast::Expr::Str(StrLiteral::Line(segments)) => {
segments.iter().all(|segment| match segment { segments.iter().all(|segment| match segment {
@ -2453,6 +2457,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
.iter() .iter()
.all(|loc_field| is_valid_interpolation(&loc_field.value)), .all(|loc_field| is_valid_interpolation(&loc_field.value)),
ast::Expr::MultipleRecordBuilders(loc_expr) ast::Expr::MultipleRecordBuilders(loc_expr)
| ast::Expr::MalformedSuffixed(loc_expr)
| ast::Expr::UnappliedRecordBuilder(loc_expr) | ast::Expr::UnappliedRecordBuilder(loc_expr)
| ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. }) | ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. })
| ast::Expr::UnaryOp(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::SpaceBefore(_, _)
| ast::RecordBuilderField::SpaceAfter(_, _) => false, | ast::RecordBuilderField::SpaceAfter(_, _) => false,
}), }),
ast::Expr::Suffixed(_) => todo!(),
} }
} }

View File

@ -25,6 +25,7 @@ pub mod pattern;
pub mod procedure; pub mod procedure;
pub mod scope; pub mod scope;
pub mod string; pub mod string;
pub mod suffixed;
pub mod traverse; pub mod traverse;
pub use derive::DERIVED_REGION; pub use derive::DERIVED_REGION;

View File

@ -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 // 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 // operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily. // 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(); let mut rigid_variables = RigidVariables::default();

View File

@ -265,7 +265,10 @@ pub fn canonicalize_def_header_pattern<'a>(
match pattern { match pattern {
// Identifiers that shadow ability members may appear (and may only appear) at the header of a def. // 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( match scope.introduce_or_shadow_ability_member(
pending_abilities_in_scope, pending_abilities_in_scope,
(*name).into(), (*name).into(),
@ -373,12 +376,13 @@ pub fn canonicalize_pattern<'a>(
use PatternType::*; use PatternType::*;
let can_pattern = match pattern { let can_pattern = match pattern {
Identifier(name) => { Identifier {
match canonicalize_pattern_symbol(env, scope, output, region, permit_shadows, name) { ident: name,
Ok(symbol) => Pattern::Identifier(symbol), suffixed: _,
Err(pattern) => pattern, } => match canonicalize_pattern_symbol(env, scope, output, region, permit_shadows, name) {
} Ok(symbol) => Pattern::Identifier(symbol),
} Err(pattern) => pattern,
},
Underscore(name) => { Underscore(name) => {
// An underscored identifier can't be used, but we'll still add it to the scope // 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. // 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() { for loc_pattern in patterns.iter() {
match loc_pattern.value { match loc_pattern.value {
Identifier(label) => { Identifier {
ident: label,
suffixed: _,
} => {
match scope.introduce(label.into(), region) { match scope.introduce(label.into(), region) {
Ok(symbol) => { Ok(symbol) => {
output.references.insert_bound(symbol); output.references.insert_bound(symbol);

View 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,
}
}

File diff suppressed because one or more lines are too long

View File

@ -12,5 +12,6 @@ roc_collections = { path = "../collections" }
roc_module = { path = "../module" } roc_module = { path = "../module" }
roc_parse = { path = "../parse" } roc_parse = { path = "../parse" }
roc_region = { path = "../region" } roc_region = { path = "../region" }
roc_error_macros = { path = "../../error_macros" }
bumpalo.workspace = true bumpalo.workspace = true

View File

@ -196,10 +196,11 @@ impl<'a> Formattable for ValueDef<'a> {
Expect { condition, .. } => condition.is_multiline(), Expect { condition, .. } => condition.is_multiline(),
ExpectFx { condition, .. } => condition.is_multiline(), ExpectFx { condition, .. } => condition.is_multiline(),
Dbg { 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::*; use roc_parse::ast::ValueDef::*;
match self { match self {
Annotation(loc_pattern, loc_annotation) => { Annotation(loc_pattern, loc_annotation) => {
@ -238,6 +239,7 @@ impl<'a> Formattable for ValueDef<'a> {
buf.newline(); buf.newline();
fmt_body(buf, &body_pattern.value, &body_expr.value, indent); 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) { 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); // Check if this is an assignment into the unit value
buf.indent(indent); let is_unit_assignment = if let Pattern::RecordDestructure(collection) = pattern {
buf.push_str(" ="); 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() { if body.is_multiline() {
match body { match body {

View File

@ -9,8 +9,8 @@ use crate::spaces::{
use crate::Buf; use crate::Buf;
use roc_module::called_via::{self, BinOp}; use roc_module::called_via::{self, BinOp};
use roc_parse::ast::{ use roc_parse::ast::{
AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, Pattern, is_loc_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces,
RecordBuilderField, WhenBranch, Pattern, RecordBuilderField, WhenBranch,
}; };
use roc_parse::ast::{StrLiteral, StrSegment}; use roc_parse::ast::{StrLiteral, StrSegment};
use roc_parse::ident::Accessor; use roc_parse::ident::Accessor;
@ -31,6 +31,8 @@ impl<'a> Formattable for Expr<'a> {
true true
} }
MalformedSuffixed(loc_expr) => loc_expr.is_multiline(),
// These expressions never have newlines // These expressions never have newlines
Float(..) Float(..)
| Num(..) | Num(..)
@ -46,6 +48,7 @@ impl<'a> Formattable for Expr<'a> {
| Tag(_) | Tag(_)
| OpaqueRef(_) | OpaqueRef(_)
| IngestedFile(_, _) | IngestedFile(_, _)
| EmptyDefsFinal
| Crash => false, | Crash => false,
// These expressions always have newlines // These expressions always have newlines
@ -107,7 +110,6 @@ impl<'a> Formattable for Expr<'a> {
Tuple(fields) => is_collection_multiline(fields), Tuple(fields) => is_collection_multiline(fields),
RecordUpdate { fields, .. } => is_collection_multiline(fields), RecordUpdate { fields, .. } => is_collection_multiline(fields),
RecordBuilder(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) => { Str(literal) => {
fmt_str_literal(buf, *literal, indent); fmt_str_literal(buf, *literal, indent);
} }
Var { module_name, ident } => { Var {
module_name,
ident,
suffixed,
} => {
buf.indent(indent); buf.indent(indent);
if !module_name.is_empty() { if !module_name.is_empty() {
buf.push_str(module_name); buf.push_str(module_name);
@ -176,6 +182,11 @@ impl<'a> Formattable for Expr<'a> {
} }
buf.push_str(ident); buf.push_str(ident);
let count: u8 = *suffixed;
for _ in 0..count {
buf.push('!');
}
} }
Underscore(name) => { Underscore(name) => {
buf.indent(indent); buf.indent(indent);
@ -417,6 +428,9 @@ impl<'a> Formattable for Expr<'a> {
indent, indent,
); );
} }
EmptyDefsFinal => {
// no need to print anything
}
_ => { _ => {
buf.ensure_ends_with_newline(); buf.ensure_ends_with_newline();
buf.indent(indent); buf.indent(indent);
@ -433,6 +447,9 @@ impl<'a> Formattable for Expr<'a> {
buf.push(')'); buf.push(')');
} }
} }
EmptyDefsFinal => {
// no need to print anything
}
Expect(condition, continuation) => { Expect(condition, continuation) => {
fmt_expect(buf, condition, continuation, self.is_multiline(), indent); fmt_expect(buf, condition, continuation, self.is_multiline(), indent);
} }
@ -495,28 +512,74 @@ impl<'a> Formattable for Expr<'a> {
} }
} }
RecordAccess(expr, key) => { 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('.');
buf.push_str(key); buf.push_str(key);
for _ in 0..*suffix_count {
buf.push('!');
}
} }
TupleAccess(expr, key) => { 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('.');
buf.push_str(key); buf.push_str(key);
for _ in 0..*suffix_count {
buf.push('!');
}
} }
MalformedIdent(str, _) => { MalformedIdent(str, _) => {
buf.indent(indent); buf.indent(indent);
buf.push_str(str) buf.push_str(str)
} }
MalformedSuffixed(loc_expr) => {
buf.indent(indent);
loc_expr.format_with_options(buf, parens, newlines, indent);
}
MalformedClosure => {} MalformedClosure => {}
PrecedenceConflict { .. } => {} PrecedenceConflict { .. } => {}
MultipleRecordBuilders { .. } => {} MultipleRecordBuilders { .. } => {}
UnappliedRecordBuilder { .. } => {} UnappliedRecordBuilder { .. } => {}
IngestedFile(_, _) => {} 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() || loc_right_side.value.is_multiline()
|| lefts.iter().any(|(expr, _)| expr.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 { for (loc_left_side, loc_binop) in lefts {
let binop = loc_binop.value; 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 { if is_multiline {
buf.ensure_ends_with_newline(); buf.ensure_ends_with_newline();
buf.indent(indent); buf.indent(adjusted_indent);
} else { } else {
buf.spaces(1); buf.spaces(1);
} }
@ -743,7 +824,7 @@ fn fmt_binops<'a>(
buf.spaces(1); 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) { fn format_spaces(buf: &mut Buf, spaces: &[CommentOrNewline], newlines: Newlines, indent: u16) {

View File

@ -64,7 +64,7 @@ impl<'a> Formattable for Pattern<'a> {
} }
}, },
Pattern::Identifier(_) Pattern::Identifier { .. }
| Pattern::Tag(_) | Pattern::Tag(_)
| Pattern::OpaqueRef(_) | Pattern::OpaqueRef(_)
| Pattern::Apply(_, _) | Pattern::Apply(_, _)
@ -88,9 +88,16 @@ impl<'a> Formattable for Pattern<'a> {
use self::Pattern::*; use self::Pattern::*;
match self { match self {
Identifier(string) => { Identifier {
ident: string,
suffixed,
} => {
buf.indent(indent); buf.indent(indent);
buf.push_str(string) buf.push_str(string);
for _ in 0..*suffixed {
buf.push('!');
}
} }
Tag(name) | OpaqueRef(name) => { Tag(name) | OpaqueRef(name) => {
buf.indent(indent); buf.indent(indent);
@ -270,13 +277,21 @@ impl<'a> Formattable for Pattern<'a> {
buf.indent(indent); buf.indent(indent);
buf.push_str(string); buf.push_str(string);
} }
QualifiedIdentifier { module_name, ident } => { QualifiedIdentifier {
module_name,
ident,
suffixed,
} => {
buf.indent(indent); buf.indent(indent);
if !module_name.is_empty() { if !module_name.is_empty() {
buf.push_str(module_name); buf.push_str(module_name);
buf.push('.'); buf.push('.');
} }
for _ in 0..*suffixed {
buf.push('!');
}
buf.push_str(ident); buf.push_str(ident);
} }
} }

View File

@ -567,6 +567,7 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
condition: arena.alloc(condition.remove_spaces(arena)), condition: arena.alloc(condition.remove_spaces(arena)),
preceding_comment: Region::zero(), 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> { impl<'a> RemoveSpaces<'a> for Expr<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self { fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self { match *self {
Expr::EmptyDefsFinal => Expr::EmptyDefsFinal,
Expr::Float(a) => Expr::Float(a), Expr::Float(a) => Expr::Float(a),
Expr::Num(a) => Expr::Num(a), Expr::Num(a) => Expr::Num(a),
Expr::NonBase10Int { Expr::NonBase10Int {
@ -692,7 +694,15 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)), Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
Expr::RecordBuilder(a) => Expr::RecordBuilder(a.remove_spaces(arena)), Expr::RecordBuilder(a) => Expr::RecordBuilder(a.remove_spaces(arena)),
Expr::Tuple(a) => Expr::Tuple(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::Underscore(a) => Expr::Underscore(a),
Expr::Tag(a) => Expr::Tag(a), Expr::Tag(a) => Expr::Tag(a),
Expr::OpaqueRef(a) => Expr::OpaqueRef(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::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)),
Expr::MalformedClosure => Expr::MalformedClosure, Expr::MalformedClosure => Expr::MalformedClosure,
Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a),
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
Expr::MultipleRecordBuilders(a) => Expr::MultipleRecordBuilders(a), Expr::MultipleRecordBuilders(a) => Expr::MultipleRecordBuilders(a),
Expr::UnappliedRecordBuilder(a) => Expr::UnappliedRecordBuilder(a), Expr::UnappliedRecordBuilder(a) => Expr::UnappliedRecordBuilder(a),
Expr::SpaceBefore(a, _) => a.remove_spaces(arena), Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
Expr::SpaceAfter(a, _) => a.remove_spaces(arena), Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
Expr::SingleQuote(a) => Expr::Num(a), 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> { impl<'a> RemoveSpaces<'a> for Pattern<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self { fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self { match *self {
Pattern::Identifier(a) => Pattern::Identifier(a), Pattern::Identifier { ident, suffixed } => Pattern::Identifier { ident, suffixed },
Pattern::Tag(a) => Pattern::Tag(a), Pattern::Tag(a) => Pattern::Tag(a),
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a), Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
Pattern::Apply(a, b) => Pattern::Apply( Pattern::Apply(a, b) => Pattern::Apply(
@ -825,9 +835,15 @@ impl<'a> RemoveSpaces<'a> for Pattern<'a> {
Pattern::Underscore(a) => Pattern::Underscore(a), Pattern::Underscore(a) => Pattern::Underscore(a),
Pattern::Malformed(a) => Pattern::Malformed(a), Pattern::Malformed(a) => Pattern::Malformed(a),
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)), Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
Pattern::QualifiedIdentifier { module_name, ident } => { Pattern::QualifiedIdentifier {
Pattern::QualifiedIdentifier { module_name, ident } module_name,
} ident,
suffixed,
} => Pattern::QualifiedIdentifier {
module_name,
ident,
suffixed,
},
Pattern::SpaceBefore(a, _) => a.remove_spaces(arena), Pattern::SpaceBefore(a, _) => a.remove_spaces(arena),
Pattern::SpaceAfter(a, _) => a.remove_spaces(arena), Pattern::SpaceAfter(a, _) => a.remove_spaces(arena),
Pattern::SingleQuote(a) => Pattern::SingleQuote(a), Pattern::SingleQuote(a) => Pattern::SingleQuote(a),

View File

@ -4768,33 +4768,38 @@ mod test_reporting {
" "
); );
test_report!( // TODO investigate this test. It was disabled in https://github.com/roc-lang/roc/pull/6634
def_missing_final_expression, // as the way Defs without final expressions are handled. The changes probably shouldn't have
indoc!( // changed this error report. The exact same test_syntax test for this has not changed, so
r" // we know the parser is parsing thesame thing. Therefore the way the AST is desugared must be
f : Foo.foo // the cause of the change in error report.
" // test_report!(
), // def_missing_final_expression,
@r#" // indoc!(
MISSING FINAL EXPRESSION in tmp/def_missing_final_expression/Test.roc // 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" // 1│ app "test" provides [main] to "./platform"
2 // 2│
3 main = // 3│ main =
4 f : Foo.foo // 4│ f : Foo.foo
^ // ^
This definition is missing a final expression. A nested definition // This definition is missing a final expression. A nested definition
must be followed by either another definition, or an expression // must be followed by either another definition, or an expression
x = 4 // x = 4
y = 2 // y = 2
x + y // x + y
"# // "#
); // );
test_report!( test_report!(
expression_indentation_end, expression_indentation_end,

View File

@ -212,7 +212,11 @@ fn generate_entry_docs(
match either_index.split() { match either_index.split() {
Err(value_index) => match &defs.value_defs[value_index.index()] { Err(value_index) => match &defs.value_defs[value_index.index()] {
ValueDef::Annotation(loc_pattern, loc_ann) => { 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 // Check if this module exposes the def
if let Some(ident_id) = ident_ids.get_id(identifier) { if let Some(ident_id) = ident_ids.get_id(identifier) {
let name = identifier.to_string(); let name = identifier.to_string();
@ -233,7 +237,11 @@ fn generate_entry_docs(
ann_type, 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 // Check if this module exposes the def
if let Some(ident_id) = ident_ids.get_id(identifier) { if let Some(ident_id) = ident_ids.get_id(identifier) {
let doc_def = DocDef { let doc_def = DocDef {
@ -249,7 +257,11 @@ fn generate_entry_docs(
} }
ValueDef::Body(pattern, _) => { 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 // Check if this module exposes the def
if let Some(ident_id) = ident_ids.get_id(identifier) { if let Some(ident_id) = ident_ids.get_id(identifier) {
let doc_def = DocDef { let doc_def = DocDef {
@ -275,6 +287,25 @@ fn generate_entry_docs(
ValueDef::ExpectFx { .. } => { ValueDef::ExpectFx { .. } => {
// Don't generate docs for `expect-fx`s // 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()] { Ok(type_index) => match &defs.type_defs[type_index.index()] {
@ -285,7 +316,11 @@ fn generate_entry_docs(
let mut type_vars = Vec::new(); let mut type_vars = Vec::new();
for var in vars.iter() { 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()); type_vars.push(ident_name.to_string());
} }
} }
@ -319,7 +354,11 @@ fn generate_entry_docs(
let mut type_vars = Vec::new(); let mut type_vars = Vec::new();
for var in vars.iter() { 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()); type_vars.push(ident_name.to_string());
} }
} }
@ -343,7 +382,11 @@ fn generate_entry_docs(
let mut type_vars = Vec::new(); let mut type_vars = Vec::new();
for var in vars.iter() { 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()); 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 .vars
.iter() .iter()
.filter_map(|loc_pattern| match loc_pattern.value { .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, _ => None,
}) })
.collect(), .collect(),

View File

@ -5647,7 +5647,15 @@ fn value_def_from_imports<'a>(
); );
}; };
let typed_ident = typed_ident.extract_spaces().item; 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); let ann_type = arena.alloc(typed_ident.ann);
Some(ValueDef::AnnotatedBody { Some(ValueDef::AnnotatedBody {
ann_pattern: ident, ann_pattern: ident,

View File

@ -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( let compare_values = tag_id_a_stmt(root.arena.alloc(
// //

View File

@ -7,6 +7,7 @@ use crate::parser::ESingleQuote;
use bumpalo::collections::{String, Vec}; use bumpalo::collections::{String, Vec};
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::soa::{EitherIndex, Index, Slice}; use roc_collections::soa::{EitherIndex, Index, Slice};
use roc_error_macros::internal_error;
use roc_module::called_via::{BinOp, CalledVia, UnaryOp}; use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position, Region};
@ -267,9 +268,6 @@ pub enum Expr<'a> {
// Collection Literals // Collection Literals
List(Collection<'a, &'a Loc<Expr<'a>>>), List(Collection<'a, &'a Loc<Expr<'a>>>),
/// An expression followed by `!``
Suffixed(&'a Expr<'a>),
RecordUpdate { RecordUpdate {
update: &'a Loc<Expr<'a>>, update: &'a Loc<Expr<'a>>,
fields: Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>, fields: Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>,
@ -289,6 +287,7 @@ pub enum Expr<'a> {
Var { 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` 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, ident: &'a str,
suffixed: u8, // how many `!` suffixes, for example `doTheThing!!` executes a Task that returns a Task
}, },
Underscore(&'a str), Underscore(&'a str),
@ -306,6 +305,11 @@ pub enum Expr<'a> {
Closure(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>), Closure(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>),
/// Multiple defs in a row /// Multiple defs in a row
Defs(&'a Defs<'a>, &'a Loc<Expr<'a>>), 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>>), Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Expect(&'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>>), Dbg(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
@ -341,6 +345,7 @@ pub enum Expr<'a> {
// Problems // Problems
MalformedIdent(&'a str, crate::ident::BadIdent), MalformedIdent(&'a str, crate::ident::BadIdent),
MalformedClosure, MalformedClosure,
MalformedSuffixed(&'a Loc<Expr<'a>>),
// Both operators were non-associative, e.g. (True == False == False). // Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens. // We should tell the author to disambiguate by grouping them with parens.
PrecedenceConflict(&'a PrecedenceConflict<'a>), PrecedenceConflict(&'a PrecedenceConflict<'a>),
@ -348,6 +353,110 @@ pub enum Expr<'a> {
UnappliedRecordBuilder(&'a Loc<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, &region);
}
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)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct PrecedenceConflict<'a> { pub struct PrecedenceConflict<'a> {
pub whole_region: Region, pub whole_region: Region,
@ -458,6 +567,18 @@ pub enum ValueDef<'a> {
condition: &'a Loc<Expr<'a>>, condition: &'a Loc<Expr<'a>>,
preceding_comment: Region, 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)] #[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>>> { pub fn last(&self) -> Option<Result<&TypeDef<'a>, &ValueDef<'a>>> {
self.tags.last().map(|tag| match tag.split() { self.tags.last().map(|tag| match tag.split() {
Ok(type_index) => Ok(&self.type_defs[type_index.index()]), 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) { pub fn remove_value_def(&mut self, index: usize) {
match self match self
.tags .tags
@ -567,6 +739,7 @@ impl<'a> Defs<'a> {
self.push_def_help(tag, region, spaces_before, spaces_after) self.push_def_help(tag, region, spaces_before, spaces_after)
} }
/// Replace the `value_def` at the given index
pub fn replace_with_value_def( pub fn replace_with_value_def(
&mut self, &mut self,
index: usize, index: usize,
@ -592,51 +765,81 @@ impl<'a> Defs<'a> {
self.push_def_help(tag, region, spaces_before, spaces_after) self.push_def_help(tag, region, spaces_before, spaces_after)
} }
// Find the first definition that is a Apply Suffixed /// Split the defs around a given target index
// 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 /// This is useful for unwrapping suffixed `!`
pub fn search_suffixed_defs(&self) -> Option<(usize, usize)> { pub fn split_defs_around(&self, target: usize) -> SplitDefsAround<'a> {
for (tag_index, tag) in self.tags.iter().enumerate() { let mut before = Defs::default();
if let Err(value_index) = tag.split() { let mut after = Defs::default();
let index = value_index.index();
if let ValueDef::Body(_, expr) = &self.value_defs[index] { for (tag_index, tag) in self.tags.iter().enumerate() {
// The Suffixed has arguments applied e.g. `Stdout.line! "Hello World"` let region = self.regions[tag_index];
if let Expr::Apply(sub_expr, _, _) = expr.value { let space_before = {
if let Expr::Suffixed(_) = sub_expr.value { let start = self.space_before[tag_index].start();
return Some((tag_index, index)); 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!` match tag_index.cmp(&target) {
if let Expr::Suffixed(_) = expr.value { std::cmp::Ordering::Less => {
return Some((tag_index, index)); // 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 } SplitDefsAround { before, after }
} }
} }
#[derive(Debug, Clone, PartialEq)]
pub struct SplitDefsAround<'a> { pub struct SplitDefsAround<'a> {
pub before: Defs<'a>, pub before: Defs<'a>,
pub after: Defs<'a>, pub after: Defs<'a>,
@ -871,7 +1074,15 @@ impl<'a> PatternAs<'a> {
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Pattern<'a> { pub enum Pattern<'a> {
// Identifier // Identifier
Identifier(&'a str), Identifier {
ident: &'a str,
suffixed: u8,
},
QualifiedIdentifier {
module_name: &'a str,
ident: &'a str,
suffixed: u8,
},
Tag(&'a str), Tag(&'a str),
@ -923,10 +1134,6 @@ pub enum Pattern<'a> {
// Malformed // Malformed
Malformed(&'a str), Malformed(&'a str),
MalformedIdent(&'a str, crate::ident::BadIdent), MalformedIdent(&'a str, crate::ident::BadIdent),
QualifiedIdentifier {
module_name: &'a str,
ident: &'a str,
},
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[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 } : { x : Int, y ? Bool }
// { x, y ? False } = rec // { x, y ? False } = rec
OptionalField(x, _) => match other { OptionalField(x, _) => match other {
Identifier(y) | OptionalField(y, _) => x == y, Identifier {
ident: y,
suffixed: 0,
}
| OptionalField(y, _) => x == y,
_ => false, _ => false,
}, },
Identifier(x) => match other { Identifier {
Identifier(y) | OptionalField(y, _) => x == y, ident: x,
suffixed: a,
} => match other {
Identifier {
ident: y,
suffixed: b,
} => x == y && a == b,
OptionalField(y, _) => x == y,
_ => false, _ => false,
}, },
NumLiteral(x) => { NumLiteral(x) => {
@ -1057,13 +1275,15 @@ impl<'a> Pattern<'a> {
QualifiedIdentifier { QualifiedIdentifier {
module_name: a, module_name: a,
ident: x, ident: x,
suffixed: i,
} => { } => {
if let QualifiedIdentifier { if let QualifiedIdentifier {
module_name: b, module_name: b,
ident: y, ident: y,
suffixed: j,
} = other } = other
{ {
a == b && x == y a == b && x == y && i == j
} else { } else {
false 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)] #[derive(Copy, Clone)]
pub struct Collection<'a, T> { pub struct Collection<'a, T> {
@ -1384,11 +1613,13 @@ impl<'a> Expr<'a> {
pub const REPL_OPAQUE_FUNCTION: Self = Expr::Var { pub const REPL_OPAQUE_FUNCTION: Self = Expr::Var {
module_name: "", module_name: "",
ident: "<function>", ident: "<function>",
suffixed: 0,
}; };
pub const REPL_RUNTIME_CRASH: Self = Expr::Var { pub const REPL_RUNTIME_CRASH: Self = Expr::Var {
module_name: "", module_name: "",
ident: "*", ident: "*",
suffixed: 0,
}; };
pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> { pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> {
@ -1617,6 +1848,7 @@ impl<'a> Malformed for Expr<'a> {
OpaqueRef(_) | OpaqueRef(_) |
SingleQuote(_) | // This is just a &str - not a bunch of segments SingleQuote(_) | // This is just a &str - not a bunch of segments
IngestedFile(_, _) | IngestedFile(_, _) |
EmptyDefsFinal |
Crash => false, Crash => false,
Str(inner) => inner.is_malformed(), Str(inner) => inner.is_malformed(),
@ -1650,10 +1882,10 @@ impl<'a> Malformed for Expr<'a> {
MalformedIdent(_, _) | MalformedIdent(_, _) |
MalformedClosure | MalformedClosure |
MalformedSuffixed(..) |
PrecedenceConflict(_) | PrecedenceConflict(_) |
MultipleRecordBuilders(_) | MultipleRecordBuilders(_) |
UnappliedRecordBuilder(_) => true, UnappliedRecordBuilder(_) => true,
Suffixed(expr) => expr.is_malformed(),
} }
} }
} }
@ -1748,7 +1980,7 @@ impl<'a> Malformed for Pattern<'a> {
use Pattern::*; use Pattern::*;
match self { match self {
Identifier(_) | Identifier{ .. } |
Tag(_) | Tag(_) |
OpaqueRef(_) => false, OpaqueRef(_) => false,
Apply(func, args) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()), Apply(func, args) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()),
@ -1888,6 +2120,7 @@ impl<'a> Malformed for ValueDef<'a> {
condition, condition,
preceding_comment: _, preceding_comment: _,
} => condition.is_malformed(), } => condition.is_malformed(),
ValueDef::Stmt(loc_expr) => loc_expr.is_malformed(),
} }
} }
} }

View File

@ -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 where
E: 'a, E: 'a,
{ {
let extra_spaces = if inside_suffixed_statement { 1 } else { 0 };
move |_, state: State<'a>, min_indent: u32| { move |_, state: State<'a>, min_indent: u32| {
if state.column() >= min_indent { if state.column() >= (min_indent + extra_spaces) {
Ok((NoProgress, (), state)) Ok((NoProgress, (), state))
} else { } else {
Err((NoProgress, indent_problem(state.pos()))) Err((NoProgress, indent_problem(state.pos())))

View File

@ -1,7 +1,7 @@
use crate::ast::{ use crate::ast::{
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements, is_loc_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
ImplementsAbilities, Pattern, RecordBuilderField, Spaceable, Spaces, TypeAnnotation, TypeDef, Implements, ImplementsAbilities, Pattern, RecordBuilderField, Spaceable, Spaces,
TypeHeader, ValueDef, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
}; };
use crate::blankspace::{ use crate::blankspace::{
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e, 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 -> ... /// > Just foo if foo == 2 -> ...
pub check_for_arrow: bool, 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>> { 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>> { 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| { line_min_indent(move |arena, state: State<'a>, min_indent: u32| {
let (_, expr, state) = loc_possibly_negative_or_negated_term(options) let (_, expr, state) =
.parse(arena, state, min_indent) 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 initial_state = state.clone(); let initial_state = state.clone();
let end = state.pos(); 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) { match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) {
Err((_, _)) => Ok((MadeProgress, expr.value, state)), Err((_, _)) => Ok((MadeProgress, expr.value, state)),
Ok((_, spaces_before_op, state)) => { Ok((_, spaces_before_op, state)) => {
@ -350,7 +358,31 @@ fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a
end, 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() } else if !self.expr.value.is_tag()
&& !self.expr.value.is_opaque() && !self.expr.value.is_opaque()
&& !self.arguments.is_empty() && !self.arguments.is_empty()
&& !is_loc_expr_suffixed(&self.expr)
{ {
let region = Region::across_all(self.arguments.iter().map(|v| &v.region)); let region = Region::across_all(self.arguments.iter().map(|v| &v.region));
@ -623,8 +656,26 @@ pub fn parse_single_def<'a>(
} }
} }
Err((MadeProgress, _)) => { Err((MadeProgress, _)) => {
// a hacky way to get expression-based error messages. TODO fix this // Try to parse as a Statement
Ok((NoProgress, None, initial)) 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)) => { Ok((_, loc_pattern, state)) => {
// First let's check whether this is an ability definition. // 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. // This may be a def or alias.
match operator().parse(arena, state, min_indent) { let operator_result = operator().parse(arena, state.clone(), min_indent);
Ok((_, BinOp::Assignment, state)) => {
let parse_def_expr = space0_before_e(
increment_min_indent(expr_start(options)),
EExpr::IndentEnd,
);
let (_, loc_def_expr, state) = if let Ok((_, BinOp::Assignment, operator_result_state)) = operator_result {
parse_def_expr.parse(arena, state, min_indent)?; return parse_single_def_assignment(
let value_def = options,
ValueDef::Body(arena.alloc(loc_pattern), &*arena.alloc(loc_def_expr)); // to support statements we have to increase the indent here so that we can parse a child def
let region = Region::span_across(&loc_pattern.region, &loc_def_expr.region); // 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(( if let Ok((_, BinOp::IsAliasType, state)) = operator_result {
MadeProgress, // the increment_min_indent here is probably _wrong_, since alias_signature_with_space_before does
Some(SingleDef { // that internally.
type_or_value: Either::Second(value_def), // TODO: re-evaluate this
region, let parser = increment_min_indent(alias_signature_with_space_before());
spaces_before: spaces_before_current, let (_, ann_type, state) = parser.parse(arena, state, min_indent)?;
}), let region = Region::span_across(&loc_pattern.region, &ann_type.region);
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);
match &loc_pattern.value.extract_spaces().item { match &loc_pattern.value.extract_spaces().item {
Pattern::Apply( Pattern::Apply(
Loc { Loc {
value: Pattern::Tag(name), value: Pattern::Tag(name),
.. ..
}, },
alias_arguments, alias_arguments,
) => { ) => {
let name = Loc::at(loc_pattern.region, *name); let name = Loc::at(loc_pattern.region, *name);
let header = TypeHeader { let header = TypeHeader {
name, name,
vars: alias_arguments, vars: alias_arguments,
}; };
let type_def = TypeDef::Alias { let type_def = TypeDef::Alias {
header, header,
ann: ann_type, ann: ann_type,
}; };
Ok(( return Ok((
MadeProgress, MadeProgress,
Some(SingleDef { Some(SingleDef {
type_or_value: Either::First(type_def), type_or_value: Either::First(type_def),
region, region,
spaces_before: spaces_before_current, spaces_before: spaces_before_current,
}), }),
state, state,
)) ));
} }
Pattern::Tag(name) => { Pattern::Tag(name) => {
let name = Loc::at(loc_pattern.region, *name); let name = Loc::at(loc_pattern.region, *name);
let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[]; let pattern_arguments: &'a [Loc<Pattern<'a>>] = &[];
let header = TypeHeader { let header = TypeHeader {
name, name,
vars: pattern_arguments, vars: pattern_arguments,
}; };
let type_def = TypeDef::Alias { let type_def = TypeDef::Alias {
header, header,
ann: ann_type, ann: ann_type,
}; };
Ok(( return Ok((
MadeProgress, MadeProgress,
Some(SingleDef { Some(SingleDef {
type_or_value: Either::First(type_def), type_or_value: Either::First(type_def),
region, region,
spaces_before: spaces_before_current, spaces_before: spaces_before_current,
}), }),
state, state,
)) ));
} }
_ => { _ => {
let value_def = ValueDef::Annotation(loc_pattern, ann_type); let value_def = ValueDef::Annotation(loc_pattern, ann_type);
Ok(( return Ok((
MadeProgress, MadeProgress,
Some(SingleDef { Some(SingleDef {
type_or_value: Either::Second(value_def), type_or_value: Either::Second(value_def),
region, region,
spaces_before: spaces_before_current, spaces_before: spaces_before_current,
}), }),
state, 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 { if let Ok((_, BinOp::IsOpaqueType, state)) = operator_result {
Pattern::Apply( let (_, (signature, derived), state) =
Loc { opaque_signature_with_space_before().parse(arena, state, min_indent + 1)?;
value: Pattern::Tag(name), let region = Region::span_across(&loc_pattern.region, &signature.region);
..
},
alias_arguments,
) => {
let name = Loc::at(loc_pattern.region, *name);
let header = TypeHeader {
name,
vars: alias_arguments,
};
let type_def = TypeDef::Opaque { match &loc_pattern.value.extract_spaces().item {
header, Pattern::Apply(
typ: signature, Loc {
derived, value: Pattern::Tag(name),
}; ..
},
alias_arguments,
) => {
let name = Loc::at(loc_pattern.region, *name);
let header = TypeHeader {
name,
vars: alias_arguments,
};
Ok(( let type_def = TypeDef::Opaque {
MadeProgress, header,
Some(SingleDef { typ: signature,
type_or_value: Either::First(type_def), derived,
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 { return Ok((
header, MadeProgress,
typ: signature, Some(SingleDef {
derived, 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(( let type_def = TypeDef::Opaque {
MadeProgress, header,
Some(SingleDef { typ: signature,
type_or_value: Either::First(type_def), derived,
region, };
spaces_before: spaces_before_current,
}),
state,
))
}
_ => {
let value_def = ValueDef::Annotation(loc_pattern, signature);
Ok(( return Ok((
MadeProgress, MadeProgress,
Some(SingleDef { Some(SingleDef {
type_or_value: Either::Second(value_def), type_or_value: Either::First(type_def),
region, region,
spaces_before: spaces_before_current, spaces_before: spaces_before_current,
}), }),
state, 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` /// 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)] #[allow(clippy::too_many_arguments)]
fn parse_statement_inside_def<'a>( 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 preceding_comment = Region::new(spaces_before_current_start, start);
let value_def = get_value_def(preceding_comment, loc_def_expr); let value_def = get_value_def(preceding_comment, loc_def_expr);
Ok(( Ok((
@ -960,6 +1118,9 @@ fn parse_defs_end<'a>(
let mut global_state = state; let mut global_state = state;
loop { loop {
// keep a copy in the event we get an EExpr::DefMissingFinalExpr
let state_before = global_state.clone();
let state = global_state; let state = global_state;
global_state = match parse_single_def(options, min_indent, arena, state) { global_state = match parse_single_def(options, min_indent, arena, state) {
@ -1039,11 +1200,16 @@ fn parse_defs_end<'a>(
next_state next_state
} }
Ok((progress, None, s)) => return Ok((progress, defs, s)), 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)), Err((progress, err)) => return Err((progress, err)),
}; };
} }
} }
#[derive(Debug)]
pub struct SingleDef<'a> { pub struct SingleDef<'a> {
pub type_or_value: Either<TypeDef<'a>, ValueDef<'a>>, pub type_or_value: Either<TypeDef<'a>, ValueDef<'a>>,
pub region: Region, pub region: Region,
@ -1065,18 +1231,50 @@ fn parse_defs_expr<'a>(
match parse_final_expr.parse(arena, state.clone(), min_indent) { match parse_final_expr.parse(arena, state.clone(), min_indent) {
Err((_, fail)) => { 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, MadeProgress,
EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.pos()), 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.end = new_end;
expr_state.spaces_after = spaces; expr_state.spaces_after = spaces;
// TODO new start? let new_options = if is_loc_expr_suffixed(&new_expr) {
parse_expr_end(min_indent, options, expr_state, arena, state, initial_state) 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>, initial_state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let parser = skip_first!( let parser = skip_first!(
crate::blankspace::check_indent(EExpr::IndentEnd), crate::blankspace::check_indent(EExpr::IndentEnd, options.suffixed_found),
loc_term_or_underscore(options) loc_term_or_underscore(options)
); );
@ -1667,6 +1908,7 @@ fn parse_expr_end<'a>(
Expr::Var { Expr::Var {
module_name: "", module_name: "",
ident: crate::keyword::IMPLEMENTS, ident: crate::keyword::IMPLEMENTS,
..
}, },
.. ..
}, },
@ -1715,6 +1957,12 @@ fn parse_expr_end<'a>(
Ok((_, mut arg, state)) => { Ok((_, mut arg, state)) => {
let new_end = state.pos(); 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` // now that we have `function arg1 ... <spaces> argn`, attach the spaces to the `argn`
if !expr_state.spaces_after.is_empty() { if !expr_state.spaces_after.is_empty() {
arg = arena arg = arena
@ -1739,7 +1987,14 @@ fn parse_expr_end<'a>(
expr_state.end = new_end; expr_state.end = new_end;
expr_state.spaces_after = new_spaces; 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 { expr_start(ExprParseOptions {
accept_multi_backpassing, accept_multi_backpassing,
check_for_arrow: true, 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 { let mut pat = match expr.item {
Expr::Var { module_name, ident } => { Expr::Var {
module_name,
ident,
suffixed,
} => {
if module_name.is_empty() { if module_name.is_empty() {
Pattern::Identifier(ident) Pattern::Identifier { ident, suffixed }
} else { } else {
Pattern::QualifiedIdentifier { module_name, ident } Pattern::QualifiedIdentifier {
module_name,
ident,
suffixed,
}
} }
} }
Expr::Underscore(opt_name) => Pattern::Underscore(opt_name), 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::SpaceBefore(..)
| Expr::SpaceAfter(..) | Expr::SpaceAfter(..)
| Expr::ParensAround(..) | Expr::ParensAround(..)
| Expr::RecordBuilder(..) => unreachable!(), | Expr::RecordBuilder(..)
| Expr::EmptyDefsFinal => unreachable!(),
Expr::Record(fields) => { Expr::Record(fields) => {
let patterns = fields.map_items_result(arena, |loc_assigned_field| { 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::Dbg(_, _)
| Expr::LowLevelDbg(_, _, _) | Expr::LowLevelDbg(_, _, _)
| Expr::MalformedClosure | Expr::MalformedClosure
| Expr::MalformedSuffixed(..)
| Expr::PrecedenceConflict { .. } | Expr::PrecedenceConflict { .. }
| Expr::MultipleRecordBuilders { .. } | Expr::MultipleRecordBuilders { .. }
| Expr::UnappliedRecordBuilder { .. } | 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::Str(string) => Pattern::StrLiteral(string),
Expr::SingleQuote(string) => Pattern::SingleQuote(string), Expr::SingleQuote(string) => Pattern::SingleQuote(string),
Expr::MalformedIdent(string, problem) => Pattern::MalformedIdent(string, problem), Expr::MalformedIdent(string, problem) => Pattern::MalformedIdent(string, problem),
Expr::Suffixed(_) => todo!(),
}; };
// Now we re-add the spaces // 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( AssignedField::SpaceBefore(nested, spaces) => Pattern::SpaceBefore(
arena.alloc(assigned_expr_field_to_pattern_help(arena, nested)?), arena.alloc(assigned_expr_field_to_pattern_help(arena, nested)?),
spaces, spaces,
@ -2050,6 +2318,7 @@ pub fn toplevel_defs<'a>() -> impl Parser<'a, Defs<'a>, EExpr<'a>> {
let options = ExprParseOptions { let options = ExprParseOptions {
accept_multi_backpassing: true, accept_multi_backpassing: true,
check_for_arrow: true, check_for_arrow: true,
suffixed_found: false,
}; };
let mut output = Defs::default(); 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, // The first value in the iterator is the variable name,
// e.g. `foo` in `foo.bar.baz` // e.g. `foo` in `foo.bar.baz`
let mut answer = match iter.next() { let mut answer = match iter.next() {
Some(Accessor::RecordField(ident)) if suffixed => { Some(Accessor::RecordField(ident)) => Expr::Var {
Expr::Suffixed(arena.alloc(Expr::Var { module_name, ident })) module_name,
} ident,
Some(Accessor::RecordField(ident)) => Expr::Var { module_name, ident }, suffixed,
},
Some(Accessor::TupleIndex(_)) => { Some(Accessor::TupleIndex(_)) => {
// TODO: make this state impossible to represent in Ident::Access, // 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`, // by splitting out parts[0] into a separate field with a type of `&'a str`,

View File

@ -42,7 +42,7 @@ pub enum Ident<'a> {
Access { Access {
module_name: &'a str, module_name: &'a str,
parts: &'a [Accessor<'a>], parts: &'a [Accessor<'a>],
suffixed: bool, suffixed: u8,
}, },
/// `.foo { foo: 42 }` or `.1 (1, 2, 3)` /// `.foo { foo: 42 }` or `.1 (1, 2, 3)`
AccessorFunction(Accessor<'a>), AccessorFunction(Accessor<'a>),
@ -194,7 +194,9 @@ pub fn parse_ident<'a>(
Ok((width, ident)) => { Ok((width, ident)) => {
let state = advance_state!(state, width as usize)?; let state = advance_state!(state, width as usize)?;
if let Ident::Access { if let Ident::Access {
module_name, parts, .. module_name,
parts,
suffixed,
} = ident } = ident
{ {
if module_name.is_empty() { if module_name.is_empty() {
@ -206,21 +208,16 @@ pub fn parse_ident<'a>(
} }
} }
} }
}
// Parse a suffixed `!` expression return Ok((
if state.bytes().starts_with(b"!") { MadeProgress,
if let Ident::Access { Ident::Access {
module_name, parts, ..
} = ident
{
let new_ident = Ident::Access {
module_name, module_name,
parts, parts,
suffixed: true, suffixed,
}; },
return Ok((MadeProgress, new_ident, state.advance(1))); state,
} ));
} }
Ok((MadeProgress, ident, state)) Ok((MadeProgress, ident, state))
@ -531,10 +528,22 @@ fn chomp_identifier_chain<'a>(
chomped += width as usize; 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 { let ident = Ident::Access {
module_name, module_name,
parts: parts.into_bump_slice(), parts: parts.into_bump_slice(),
suffixed: false, suffixed,
}; };
Ok((chomped as u32, ident)) Ok((chomped as u32, ident))
@ -563,15 +572,30 @@ fn chomp_identifier_chain<'a>(
} else if first_is_uppercase { } else if first_is_uppercase {
// just one segment, starting with an uppercase letter; that's a tag // just one segment, starting with an uppercase letter; that's a tag
let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) };
Ok((chomped as u32, Ident::Tag(value))) Ok((chomped as u32, Ident::Tag(value)))
} else { } else {
// just one segment, starting with a lowercase letter; that's a normal identifier // just one segment, starting with a lowercase letter; that's a normal identifier
let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; 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 { let ident = Ident::Access {
module_name: "", module_name: "",
parts: arena.alloc([Accessor::RecordField(value)]), parts: arena.alloc([Accessor::RecordField(value)]),
suffixed: false, suffixed,
}; };
Ok((chomped as u32, ident)) Ok((chomped as u32, ident))
} }
} }

View File

@ -12,6 +12,7 @@ use crate::string_literal::StrLikeLiteral;
use bumpalo::collections::string::String; use bumpalo::collections::string::String;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_error_macros::internal_error;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
/// Different patterns are supported in different circumstances. /// 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(); 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) = let (pattern_spaces, state) =
match space0_e(EPattern::AsKeyword).parse(arena, state, min_indent) { match space0_e(EPattern::AsKeyword).parse(arena, state, min_indent) {
Err(_) => return Ok((MadeProgress, pattern, pattern_state)), Err(_) => return Ok((MadeProgress, pattern, pattern_state)),
@ -138,7 +147,15 @@ fn loc_tag_pattern_arg<'a>(
let Loc { region, value } = loc_pat; 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()))) Err((NoProgress, EPattern::End(original_state.pos())))
} else { } else {
Ok(( Ok((
@ -160,7 +177,10 @@ pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPatt
|_arena, state, progress, pattern| { |_arena, state, progress, pattern| {
if matches!( if matches!(
pattern.value, pattern.value,
Pattern::Identifier(crate::keyword::IMPLEMENTS) Pattern::Identifier {
ident: crate::keyword::IMPLEMENTS,
..
}
) { ) {
Ok(( Ok((
progress, progress,
@ -385,6 +405,46 @@ fn loc_ident_pattern_help<'a>(
Ok((MadeProgress, loc_pat, state)) 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 { Ident::Access {
module_name, parts, .. module_name, parts, ..
} => { } => {
@ -403,7 +463,10 @@ fn loc_ident_pattern_help<'a>(
MadeProgress, MadeProgress,
Loc { Loc {
region: loc_ident.region, region: loc_ident.region,
value: Pattern::Identifier(var), value: Pattern::Identifier {
ident: var,
suffixed: 0,
},
}, },
state, state,
)); ));
@ -564,9 +627,18 @@ fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>>
None => { None => {
let Loc { value, region } = loc_label; let Loc { value, region } = loc_label;
let value = if !spaces.is_empty() { 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 { } else {
Pattern::Identifier(value) Pattern::Identifier {
ident: value,
suffixed: 0,
}
}; };
Ok((MadeProgress, Loc::at(region, value), state)) Ok((MadeProgress, Loc::at(region, value), state))

View File

@ -70,7 +70,13 @@ fn check_type_alias<'a>(
var_names.reserve(vars.len()); var_names.reserve(vars.len());
for var in vars { for var in vars {
if let TypeAnnotation::BoundVariable(v) = var.value { 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 { } else {
return Err(ETypeInlineAlias::ArgumentNotLowercase(var.region.start())); return Err(ETypeInlineAlias::ArgumentNotLowercase(var.region.start()));
} }

View File

@ -175,6 +175,7 @@ mod test_parse {
let expr = arena.alloc(Var { let expr = arena.alloc(Var {
module_name: "", module_name: "",
ident: "name", ident: "name",
suffixed: 0,
}); });
bumpalo::vec![in arena; bumpalo::vec![in arena;
@ -191,6 +192,7 @@ mod test_parse {
let expr = arena.alloc(Var { let expr = arena.alloc(Var {
module_name: "", module_name: "",
ident: "name", ident: "name",
suffixed: 0,
}); });
bumpalo::vec![in arena; bumpalo::vec![in arena;
@ -236,6 +238,7 @@ mod test_parse {
let expr = arena.alloc(Var { let expr = arena.alloc(Var {
module_name: "", module_name: "",
ident: "name", ident: "name",
suffixed: 0,
}); });
bumpalo::vec![in arena; bumpalo::vec![in arena;
@ -251,11 +254,13 @@ mod test_parse {
let expr1 = arena.alloc(Var { let expr1 = arena.alloc(Var {
module_name: "", module_name: "",
ident: "name", ident: "name",
suffixed: 0,
}); });
let expr2 = arena.alloc(Var { let expr2 = arena.alloc(Var {
module_name: "", module_name: "",
ident: "project", ident: "project",
suffixed: 0,
}); });
bumpalo::vec![in arena; bumpalo::vec![in arena;
@ -276,11 +281,13 @@ mod test_parse {
let expr1 = arena.alloc(Var { let expr1 = arena.alloc(Var {
module_name: "", module_name: "",
ident: "name", ident: "name",
suffixed: 0,
}); });
let expr2 = arena.alloc(Var { let expr2 = arena.alloc(Var {
module_name: "", module_name: "",
ident: "project", ident: "project",
suffixed: 0,
}); });
bumpalo::vec![in arena; bumpalo::vec![in arena;

View File

@ -356,6 +356,7 @@ impl Problem {
| Problem::RuntimeError(RuntimeError::MalformedIdentifier(_, _, region)) | Problem::RuntimeError(RuntimeError::MalformedIdentifier(_, _, region))
| Problem::RuntimeError(RuntimeError::MalformedTypeName(_, region)) | Problem::RuntimeError(RuntimeError::MalformedTypeName(_, region))
| Problem::RuntimeError(RuntimeError::MalformedClosure(region)) | Problem::RuntimeError(RuntimeError::MalformedClosure(region))
| Problem::RuntimeError(RuntimeError::MalformedSuffixed(region))
| Problem::RuntimeError(RuntimeError::InvalidRecordUpdate { region }) | Problem::RuntimeError(RuntimeError::InvalidRecordUpdate { region })
| Problem::RuntimeError(RuntimeError::InvalidFloat(_, region, _)) | Problem::RuntimeError(RuntimeError::InvalidFloat(_, region, _))
| Problem::RuntimeError(RuntimeError::InvalidInt(_, _, region, _)) | Problem::RuntimeError(RuntimeError::InvalidInt(_, _, region, _))
@ -612,6 +613,8 @@ pub enum RuntimeError {
MultipleRecordBuilders(Region), MultipleRecordBuilders(Region),
UnappliedRecordBuilder(Region), UnappliedRecordBuilder(Region),
MalformedSuffixed(Region),
} }
impl RuntimeError { impl RuntimeError {
@ -645,6 +648,7 @@ impl RuntimeError {
| RuntimeError::MalformedIdentifier(_, _, region) | RuntimeError::MalformedIdentifier(_, _, region)
| RuntimeError::MalformedTypeName(_, region) | RuntimeError::MalformedTypeName(_, region)
| RuntimeError::MalformedClosure(region) | RuntimeError::MalformedClosure(region)
| RuntimeError::MalformedSuffixed(region)
| RuntimeError::InvalidRecordUpdate { region } | RuntimeError::InvalidRecordUpdate { region }
| RuntimeError::InvalidFloat(_, region, _) | RuntimeError::InvalidFloat(_, region, _)
| RuntimeError::InvalidInt(_, _, region, _) | RuntimeError::InvalidInt(_, _, region, _)

View File

@ -2,6 +2,7 @@ When(
@5-6 Var { @5-6 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
[ [
WhenBranch { WhenBranch {

View File

@ -2,6 +2,7 @@ When(
@5-6 Var { @5-6 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
[ [
WhenBranch { WhenBranch {

View File

@ -4,6 +4,7 @@ BinOps(
@0-1 Var { @0-1 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
@2-3 Plus, @2-3 Plus,
), ),

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-3 Identifier( @0-3 Identifier {
"foo", ident: "foo",
), suffixed: 0,
},
@6-27 TagUnion { @6-27 TagUnion {
ext: None, ext: None,
tags: [ tags: [
@ -40,9 +41,10 @@ Defs(
}, },
), ),
AnnotatedBody { AnnotatedBody {
ann_pattern: @0-3 Identifier( ann_pattern: @0-3 Identifier {
"foo", ident: "foo",
), suffixed: 0,
},
ann_type: @6-27 TagUnion { ann_type: @6-27 TagUnion {
ext: None, ext: None,
tags: [ tags: [
@ -63,9 +65,10 @@ Defs(
], ],
}, },
comment: None, comment: None,
body_pattern: @28-31 Identifier( body_pattern: @28-31 Identifier {
"foo", ident: "foo",
), suffixed: 0,
},
body_expr: @34-38 Tag( body_expr: @34-38 Tag(
"True", "True",
), ),

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-3 Identifier( @0-3 Identifier {
"foo", ident: "foo",
), suffixed: 0,
},
@6-28 TagUnion { @6-28 TagUnion {
ext: Some( ext: Some(
@27-28 Wildcard, @27-28 Wildcard,
@ -42,9 +43,10 @@ Defs(
}, },
), ),
AnnotatedBody { AnnotatedBody {
ann_pattern: @0-3 Identifier( ann_pattern: @0-3 Identifier {
"foo", ident: "foo",
), suffixed: 0,
},
ann_type: @6-28 TagUnion { ann_type: @6-28 TagUnion {
ext: Some( ext: Some(
@27-28 Wildcard, @27-28 Wildcard,
@ -67,9 +69,10 @@ Defs(
], ],
}, },
comment: None, comment: None,
body_pattern: @29-32 Identifier( body_pattern: @29-32 Identifier {
"foo", ident: "foo",
), suffixed: 0,
},
body_expr: @35-39 Tag( body_expr: @35-39 Tag(
"True", "True",
), ),

View File

@ -18,12 +18,14 @@ Defs(
Annotation( Annotation(
@0-8 RecordDestructure( @0-8 RecordDestructure(
[ [
@2-3 Identifier( @2-3 Identifier {
"x", ident: "x",
), suffixed: 0,
@5-7 Identifier( },
"y", @5-7 Identifier {
), ident: "y",
suffixed: 0,
},
], ],
), ),
@11-14 Apply( @11-14 Apply(
@ -35,12 +37,14 @@ Defs(
AnnotatedBody { AnnotatedBody {
ann_pattern: @0-8 RecordDestructure( ann_pattern: @0-8 RecordDestructure(
[ [
@2-3 Identifier( @2-3 Identifier {
"x", ident: "x",
), suffixed: 0,
@5-7 Identifier( },
"y", @5-7 Identifier {
), ident: "y",
suffixed: 0,
},
], ],
), ),
ann_type: @11-14 Apply( ann_type: @11-14 Apply(
@ -51,12 +55,14 @@ Defs(
comment: None, comment: None,
body_pattern: @15-23 RecordDestructure( body_pattern: @15-23 RecordDestructure(
[ [
@17-18 Identifier( @17-18 Identifier {
"x", ident: "x",
), suffixed: 0,
@20-21 Identifier( },
"y", @20-21 Identifier {
), ident: "y",
suffixed: 0,
},
], ],
), ),
body_expr: @26-49 Record( body_expr: @26-49 Record(
@ -86,6 +92,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -18,9 +18,10 @@ Defs(
header: TypeHeader { header: TypeHeader {
name: @0-6 "UserId", name: @0-6 "UserId",
vars: [ vars: [
@7-8 Identifier( @7-8 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
}, },
ann: @11-25 TagUnion { ann: @11-25 TagUnion {
@ -47,9 +48,10 @@ Defs(
"UserId", "UserId",
), ),
[ [
@7-8 Identifier( @7-8 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
), ),
ann_type: @11-25 TagUnion { ann_type: @11-25 TagUnion {
@ -73,9 +75,10 @@ Defs(
"UserId", "UserId",
), ),
[ [
@33-34 Identifier( @33-34 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
), ),
body_expr: @37-46 Apply( body_expr: @37-46 Apply(
@ -96,6 +99,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -18,12 +18,14 @@ Defs(
Annotation( Annotation(
@0-8 Tuple( @0-8 Tuple(
[ [
@2-3 Identifier( @2-3 Identifier {
"x", ident: "x",
), suffixed: 0,
@5-6 Identifier( },
"y", @5-6 Identifier {
), ident: "y",
suffixed: 0,
},
], ],
), ),
@11-14 Apply( @11-14 Apply(
@ -35,12 +37,14 @@ Defs(
AnnotatedBody { AnnotatedBody {
ann_pattern: @0-8 Tuple( ann_pattern: @0-8 Tuple(
[ [
@2-3 Identifier( @2-3 Identifier {
"x", ident: "x",
), suffixed: 0,
@5-6 Identifier( },
"y", @5-6 Identifier {
), ident: "y",
suffixed: 0,
},
], ],
), ),
ann_type: @11-14 Apply( ann_type: @11-14 Apply(
@ -51,12 +55,14 @@ Defs(
comment: None, comment: None,
body_pattern: @15-23 Tuple( body_pattern: @15-23 Tuple(
[ [
@17-18 Identifier( @17-18 Identifier {
"x", ident: "x",
), suffixed: 0,
@20-21 Identifier( },
"y", @20-21 Identifier {
), ident: "y",
suffixed: 0,
},
], ],
), ),
body_expr: @26-41 Tuple( body_expr: @26-41 Tuple(
@ -78,6 +84,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -2,19 +2,23 @@ Apply(
@0-1 Var { @0-1 Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
[ [
@2-3 Var { @2-3 Var {
module_name: "", module_name: "",
ident: "b", ident: "b",
suffixed: 0,
}, },
@4-5 Var { @4-5 Var {
module_name: "", module_name: "",
ident: "c", ident: "c",
suffixed: 0,
}, },
@6-7 Var { @6-7 Var {
module_name: "", module_name: "",
ident: "d", ident: "d",
suffixed: 0,
}, },
], ],
Space, Space,

View File

@ -2,6 +2,7 @@ Apply(
@0-4 Var { @0-4 Var {
module_name: "", module_name: "",
ident: "whee", ident: "whee",
suffixed: 0,
}, },
[ [
@6-8 Num( @6-8 Num(

View File

@ -3,6 +3,7 @@ Apply(
@1-5 Var { @1-5 Var {
module_name: "", module_name: "",
ident: "whee", ident: "whee",
suffixed: 0,
}, },
@0-1 Negate, @0-1 Negate,
), ),
@ -13,6 +14,7 @@ Apply(
@10-13 Var { @10-13 Var {
module_name: "", module_name: "",
ident: "foo", ident: "foo",
suffixed: 0,
}, },
], ],
Space, Space,

View File

@ -3,6 +3,7 @@ Apply(
@1-5 Var { @1-5 Var {
module_name: "", module_name: "",
ident: "whee", ident: "whee",
suffixed: 0,
}, },
@0-1 Not, @0-1 Not,
), ),
@ -13,6 +14,7 @@ Apply(
@10-13 Var { @10-13 Var {
module_name: "", module_name: "",
ident: "foo", ident: "foo",
suffixed: 0,
}, },
], ],
Space, Space,

View File

@ -2,6 +2,7 @@ Apply(
@0-4 Var { @0-4 Var {
module_name: "", module_name: "",
ident: "whee", ident: "whee",
suffixed: 0,
}, },
[ [
@5-6 Num( @5-6 Num(

View File

@ -17,9 +17,10 @@ SpaceBefore(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@107-108 Identifier( @107-108 Identifier {
"x", ident: "x",
), suffixed: 0,
},
@111-112 Num( @111-112 Num(
"5", "5",
), ),

View File

@ -2,6 +2,7 @@ RecordAccess(
Var { Var {
module_name: "", module_name: "",
ident: "rec", ident: "rec",
suffixed: 0,
}, },
"field", "field",
) )

View File

@ -1,4 +1,5 @@
Var { Var {
module_name: "", module_name: "",
ident: "whee", ident: "whee",
suffixed: 0,
} }

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Identifier( @0-1 Identifier {
"a", ident: "a",
), suffixed: 0,
},
@3-4 SpaceBefore( @3-4 SpaceBefore(
BoundVariable( BoundVariable(
"c", "c",

View File

@ -3,6 +3,7 @@ Apply(
Var { Var {
module_name: "", module_name: "",
ident: "f", ident: "f",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -4,6 +4,7 @@ BinOps(
@0-1 Var { @0-1 Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
@2-4 And, @2-4 And,
), ),
@ -11,13 +12,15 @@ BinOps(
@5-12 Apply( @5-12 Apply(
@5-10 Closure( @5-10 Closure(
[ [
@6-7 Identifier( @6-7 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
@9-10 Var { @9-10 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
), ),
[ [

View File

@ -4,6 +4,7 @@ BinOps(
@0-1 Var { @0-1 Var {
module_name: "", module_name: "",
ident: "i", ident: "i",
suffixed: 0,
}, },
@1-2 GreaterThan, @1-2 GreaterThan,
), ),
@ -12,13 +13,15 @@ BinOps(
@2-7 SpaceAfter( @2-7 SpaceAfter(
Closure( Closure(
[ [
@3-4 Identifier( @3-4 Identifier {
"s", ident: "s",
), suffixed: 0,
},
], ],
@6-7 Var { @6-7 Var {
module_name: "", module_name: "",
ident: "s", ident: "s",
suffixed: 0,
}, },
), ),
[ [
@ -30,6 +33,7 @@ BinOps(
@9-10 Var { @9-10 Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
@8-9 Negate, @8-9 Negate,
), ),

View File

@ -30,6 +30,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "q", ident: "q",
suffixed: 0,
}, },
[ [
LineComment( LineComment(

View File

@ -19,9 +19,10 @@ Defs {
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-3 Identifier( @0-3 Identifier {
"foo", ident: "foo",
), suffixed: 0,
},
@6-7 Num( @6-7 Num(
"1", "1",
), ),

View File

@ -3,6 +3,7 @@ ParensAround(
Var { Var {
module_name: "", module_name: "",
ident: "i", ident: "i",
suffixed: 0,
}, },
[ [
LineComment( LineComment(

View File

@ -20,9 +20,10 @@ Defs(
vars: [ vars: [
@3-4 SpaceAfter( @3-4 SpaceAfter(
SpaceBefore( SpaceBefore(
Identifier( Identifier {
"h", ident: "h",
), suffixed: 0,
},
[ [
LineComment( LineComment(
"", "",
@ -46,6 +47,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "j", ident: "j",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -17,9 +17,10 @@ Defs(
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 SpaceAfter( @0-1 SpaceAfter(
Identifier( Identifier {
"w", ident: "w",
), suffixed: 0,
},
[ [
LineComment( LineComment(
"", "",

View File

@ -17,9 +17,10 @@ Defs(
value_defs: [ value_defs: [
Body( Body(
@0-1 SpaceAfter( @0-1 SpaceAfter(
Identifier( Identifier {
"t", ident: "t",
), suffixed: 0,
},
[ [
LineComment( LineComment(
"", "",
@ -36,6 +37,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "e", ident: "e",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -98,11 +98,13 @@ Defs(
@50-53 Var { @50-53 Var {
module_name: "", module_name: "",
ident: "try", ident: "try",
suffixed: 0,
}, },
[ [
@54-57 Var { @54-57 Var {
module_name: "", module_name: "",
ident: "foo", ident: "foo",
suffixed: 0,
}, },
@59-73 ParensAround( @59-73 ParensAround(
Closure( Closure(

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Identifier( @0-1 Identifier {
"a", ident: "a",
), suffixed: 0,
},
@2-3 BoundVariable( @2-3 BoundVariable(
"b", "b",
), ),
@ -28,5 +29,6 @@ Defs(
@4-5 Var { @4-5 Var {
module_name: "", module_name: "",
ident: "i", ident: "i",
suffixed: 0,
}, },
) )

View File

@ -21,9 +21,10 @@ Defs(
"Email", "Email",
), ),
[ [
@6-9 Identifier( @6-9 Identifier {
"str", ident: "str",
), suffixed: 0,
},
], ],
), ),
@12-36 Apply( @12-36 Apply(
@ -46,6 +47,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "str", ident: "str",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -17,9 +17,10 @@ SpaceBefore(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@48-49 Identifier( @48-49 Identifier {
"x", ident: "x",
), suffixed: 0,
},
@52-53 Num( @52-53 Num(
"5", "5",
), ),

View File

@ -2,6 +2,7 @@ RecordUpdate {
update: @1-2 Var { update: @1-2 Var {
module_name: "", module_name: "",
ident: "e", ident: "e",
suffixed: 0,
}, },
fields: [], fields: [],
} }

View File

@ -4,6 +4,7 @@ BinOps(
@0-1 Var { @0-1 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
@1-3 Equals, @1-3 Equals,
), ),
@ -11,5 +12,6 @@ BinOps(
@3-4 Var { @3-4 Var {
module_name: "", module_name: "",
ident: "y", ident: "y",
suffixed: 0,
}, },
) )

View File

@ -4,6 +4,7 @@ BinOps(
@0-1 Var { @0-1 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
@2-4 Equals, @2-4 Equals,
), ),
@ -11,5 +12,6 @@ BinOps(
@5-6 Var { @5-6 Var {
module_name: "", module_name: "",
ident: "y", ident: "y",
suffixed: 0,
}, },
) )

View File

@ -33,6 +33,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-5 Identifier( @0-5 Identifier {
"table", ident: "table",
), suffixed: 0,
},
@8-44 Function( @8-44 Function(
[ [
@8-35 Record { @8-35 Record {
@ -54,9 +55,10 @@ Defs(
), ),
), ),
AnnotatedBody { AnnotatedBody {
ann_pattern: @0-5 Identifier( ann_pattern: @0-5 Identifier {
"table", ident: "table",
), suffixed: 0,
},
ann_type: @8-44 Function( ann_type: @8-44 Function(
[ [
@8-35 Record { @8-35 Record {
@ -91,16 +93,18 @@ Defs(
), ),
), ),
comment: None, comment: None,
body_pattern: @45-50 Identifier( body_pattern: @45-50 Identifier {
"table", ident: "table",
), suffixed: 0,
},
body_expr: @53-89 Closure( body_expr: @53-89 Closure(
[ [
@54-62 RecordDestructure( @54-62 RecordDestructure(
[ [
@55-61 Identifier( @55-61 Identifier {
"height", ident: "height",
), suffixed: 0,
},
], ],
), ),
], ],
@ -123,6 +127,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "table", ident: "table",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Identifier( @0-1 Identifier {
"f", ident: "f",
), suffixed: 0,
},
@4-20 Function( @4-20 Function(
[ [
@4-10 Tuple { @4-10 Tuple {
@ -53,9 +54,10 @@ Defs(
), ),
), ),
AnnotatedBody { AnnotatedBody {
ann_pattern: @0-1 Identifier( ann_pattern: @0-1 Identifier {
"f", ident: "f",
), suffixed: 0,
},
ann_type: @4-20 Function( ann_type: @4-20 Function(
[ [
@4-10 Tuple { @4-10 Tuple {
@ -89,18 +91,21 @@ Defs(
}, },
), ),
comment: None, comment: None,
body_pattern: @21-22 Identifier( body_pattern: @21-22 Identifier {
"f", ident: "f",
), suffixed: 0,
},
body_expr: @25-32 Closure( body_expr: @25-32 Closure(
[ [
@26-27 Identifier( @26-27 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
@31-32 Var { @31-32 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
), ),
}, },
@ -111,6 +116,7 @@ Defs(
@34-35 Var { @34-35 Var {
module_name: "", module_name: "",
ident: "f", ident: "f",
suffixed: 0,
}, },
[ [
@36-47 Tuple( @36-47 Tuple(

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Identifier( @0-1 Identifier {
"f", ident: "f",
), suffixed: 0,
},
@4-21 Function( @4-21 Function(
[ [
@4-7 Apply( @4-7 Apply(
@ -45,9 +46,10 @@ Defs(
), ),
), ),
AnnotatedBody { AnnotatedBody {
ann_pattern: @0-1 Identifier( ann_pattern: @0-1 Identifier {
"f", ident: "f",
), suffixed: 0,
},
ann_type: @4-21 Function( ann_type: @4-21 Function(
[ [
@4-7 Apply( @4-7 Apply(
@ -73,20 +75,23 @@ Defs(
}, },
), ),
comment: None, comment: None,
body_pattern: @22-23 Identifier( body_pattern: @22-23 Identifier {
"f", ident: "f",
), suffixed: 0,
},
body_expr: @26-42 Closure( body_expr: @26-42 Closure(
[ [
@27-28 Identifier( @27-28 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
@32-42 Tuple( @32-42 Tuple(
[ [
@33-34 Var { @33-34 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
@36-41 BinOps( @36-41 BinOps(
[ [
@ -94,6 +99,7 @@ Defs(
@36-37 Var { @36-37 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
@38-39 Plus, @38-39 Plus,
), ),
@ -113,6 +119,7 @@ Defs(
@44-45 Var { @44-45 Var {
module_name: "", module_name: "",
ident: "f", ident: "f",
suffixed: 0,
}, },
[ [
@46-48 Num( @46-48 Num(

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-4 Identifier( @0-4 Identifier {
"iffy", ident: "iffy",
), suffixed: 0,
},
@5-6 Num( @5-6 Num(
"5", "5",
), ),

View File

@ -18,6 +18,7 @@ BinOps(
Var { Var {
module_name: "Str", module_name: "Str",
ident: "toUtf8", ident: "toUtf8",
suffixed: 0,
}, },
[ [
Newline, Newline,
@ -31,13 +32,15 @@ BinOps(
@28-36 Var { @28-36 Var {
module_name: "List", module_name: "List",
ident: "map", ident: "map",
suffixed: 0,
}, },
[ [
@37-54 Closure( @37-54 Closure(
[ [
@38-42 Identifier( @38-42 Identifier {
"byte", ident: "byte",
), suffixed: 0,
},
], ],
@46-54 BinOps( @46-54 BinOps(
[ [
@ -45,6 +48,7 @@ BinOps(
@46-50 Var { @46-50 Var {
module_name: "", module_name: "",
ident: "byte", ident: "byte",
suffixed: 0,
}, },
@51-52 Plus, @51-52 Plus,
), ),
@ -67,5 +71,6 @@ BinOps(
@58-70 Var { @58-70 Var {
module_name: "List", module_name: "List",
ident: "reverse", ident: "reverse",
suffixed: 0,
}, },
) )

View File

@ -1,8 +1,9 @@
Closure( Closure(
[ [
@1-2 Identifier( @1-2 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
@8-9 SpaceBefore( @8-9 SpaceBefore(
Num( Num(

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-6 Identifier( @0-6 Identifier {
"myList", ident: "myList",
), suffixed: 0,
},
@9-57 List( @9-57 List(
Collection { Collection {
items: [ items: [
@ -38,6 +39,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
[ [
Newline, Newline,
@ -47,6 +49,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "b", ident: "b",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-6 Identifier( @0-6 Identifier {
"myList", ident: "myList",
), suffixed: 0,
},
@9-25 List( @9-25 List(
[ [
@15-16 SpaceBefore( @15-16 SpaceBefore(

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-6 Identifier( @0-6 Identifier {
"myList", ident: "myList",
), suffixed: 0,
},
@9-26 List( @9-26 List(
Collection { Collection {
items: [ items: [

View File

@ -19,5 +19,6 @@ BinOps(
@6-7 Var { @6-7 Var {
module_name: "", module_name: "",
ident: "i", ident: "i",
suffixed: 0,
}, },
) )

View File

@ -73,18 +73,22 @@ When(
@60-72 SpaceBefore( @60-72 SpaceBefore(
List( List(
[ [
@61-62 Identifier( @61-62 Identifier {
"a", ident: "a",
), suffixed: 0,
@64-65 Identifier( },
"b", @64-65 Identifier {
), ident: "b",
@67-68 Identifier( suffixed: 0,
"c", },
), @67-68 Identifier {
@70-71 Identifier( ident: "c",
"d", suffixed: 0,
), },
@70-71 Identifier {
ident: "d",
suffixed: 0,
},
], ],
), ),
[ [
@ -102,12 +106,14 @@ When(
@81-91 SpaceBefore( @81-91 SpaceBefore(
List( List(
[ [
@82-83 Identifier( @82-83 Identifier {
"a", ident: "a",
), suffixed: 0,
@85-86 Identifier( },
"b", @85-86 Identifier {
), ident: "b",
suffixed: 0,
},
@88-90 ListRest( @88-90 ListRest(
None, None,
), ),
@ -131,12 +137,14 @@ When(
@101-103 ListRest( @101-103 ListRest(
None, None,
), ),
@105-106 Identifier( @105-106 Identifier {
"c", ident: "c",
), suffixed: 0,
@108-109 Identifier( },
"d", @108-109 Identifier {
), ident: "d",
suffixed: 0,
},
], ],
), ),
[ [
@ -170,9 +178,10 @@ When(
), ),
@131-134 List( @131-134 List(
[ [
@132-133 Identifier( @132-133 Identifier {
"a", ident: "a",
), suffixed: 0,
},
], ],
), ),
], ],
@ -207,9 +216,10 @@ When(
@156-158 List( @156-158 List(
[], [],
), ),
@160-161 Identifier( @160-161 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
), ),
], ],

View File

@ -17,9 +17,10 @@ SpaceBefore(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@113-114 Identifier( @113-114 Identifier {
"x", ident: "x",
), suffixed: 0,
},
@117-118 Num( @117-118 Num(
"5", "5",
), ),

View File

@ -15,9 +15,10 @@ Defs {
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-4 Identifier( @0-4 Identifier {
"main", ident: "main",
), suffixed: 0,
},
@11-24 SpaceBefore( @11-24 SpaceBefore(
Defs( Defs(
Defs { Defs {
@ -37,9 +38,10 @@ Defs {
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@11-12 Identifier( @11-12 Identifier {
"i", ident: "i",
), suffixed: 0,
},
@15-17 Num( @15-17 Num(
"64", "64",
), ),
@ -50,6 +52,7 @@ Defs {
Var { Var {
module_name: "", module_name: "",
ident: "i", ident: "i",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -1,16 +1,19 @@
Backpassing( Backpassing(
[ [
@0-1 Identifier( @0-1 Identifier {
"x", ident: "x",
), suffixed: 0,
@3-4 Identifier( },
"y", @3-4 Identifier {
), ident: "y",
suffixed: 0,
},
], ],
@8-23 Apply( @8-23 Apply(
@8-17 Var { @8-17 Var {
module_name: "List", module_name: "List",
ident: "map2", ident: "map2",
suffixed: 0,
}, },
[ [
@18-20 List( @18-20 List(
@ -29,6 +32,7 @@ Backpassing(
@25-26 Var { @25-26 Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
@27-28 Plus, @27-28 Plus,
), ),
@ -36,6 +40,7 @@ Backpassing(
@29-30 Var { @29-30 Var {
module_name: "", module_name: "",
ident: "y", ident: "y",
suffixed: 0,
}, },
), ),
[ [

View File

@ -15,23 +15,27 @@ Defs {
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-4 Identifier( @0-4 Identifier {
"main", ident: "main",
), suffixed: 0,
},
@12-50 SpaceBefore( @12-50 SpaceBefore(
Backpassing( Backpassing(
[ [
@12-16 Identifier( @12-16 Identifier {
"arg1", ident: "arg1",
), suffixed: 0,
@18-22 Identifier( },
"arg2", @18-22 Identifier {
), ident: "arg2",
suffixed: 0,
},
], ],
@26-30 Apply( @26-30 Apply(
@26-27 Var { @26-27 Var {
module_name: "", module_name: "",
ident: "f", ident: "f",
suffixed: 0,
}, },
[ [
@28-30 Record( @28-30 Record(

View File

@ -10,13 +10,15 @@ Backpassing(
), ),
], ],
), ),
@5-6 Identifier( @5-6 Identifier {
"r", ident: "r",
), suffixed: 0,
},
], ],
@10-11 Var { @10-11 Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
@12-13 SpaceBefore( @12-13 SpaceBefore(
Tag( Tag(

View File

@ -27,9 +27,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-1 Identifier( @0-1 Identifier {
"a", ident: "a",
), suffixed: 0,
},
@4-22 Str( @4-22 Str(
Line( Line(
[ [
@ -50,9 +51,10 @@ Defs(
), ),
), ),
Body( Body(
@23-24 Identifier( @23-24 Identifier {
"b", ident: "b",
), suffixed: 0,
},
@27-49 Str( @27-49 Str(
Block( Block(
[ [
@ -75,9 +77,10 @@ Defs(
), ),
), ),
Body( Body(
@50-51 Identifier( @50-51 Identifier {
"c", ident: "c",
), suffixed: 0,
},
@58-92 SpaceBefore( @58-92 SpaceBefore(
Str( Str(
Block( Block(

View File

@ -2,6 +2,7 @@ Apply(
@0-1 Var { @0-1 Var {
module_name: "", module_name: "",
ident: "e", ident: "e",
suffixed: 0,
}, },
[ [
@1-10 Str( @1-10 Str(

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Identifier( @0-1 Identifier {
"f", ident: "f",
), suffixed: 0,
},
@8-10 SpaceBefore( @8-10 SpaceBefore(
Record { Record {
fields: [], fields: [],

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Identifier( @0-1 Identifier {
"f", ident: "f",
), suffixed: 0,
},
@17-19 SpaceBefore( @17-19 SpaceBefore(
Record { Record {
fields: [], fields: [],

View File

@ -4,6 +4,7 @@ RecordAccess(
Var { Var {
module_name: "", module_name: "",
ident: "rec", ident: "rec",
suffixed: 0,
}, },
"abc", "abc",
), ),

View File

@ -2,6 +2,7 @@ UnaryOp(
@1-4 Var { @1-4 Var {
module_name: "", module_name: "",
ident: "inf", ident: "inf",
suffixed: 0,
}, },
@0-1 Negate, @0-1 Negate,
) )

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-1 Identifier( @0-1 Identifier {
"a", ident: "a",
), suffixed: 0,
},
@2-9 Apply( @2-9 Apply(
@2-3 SpaceAfter( @2-3 SpaceAfter(
Tag( Tag(
@ -33,12 +34,14 @@ Defs(
@6-7 Var { @6-7 Var {
module_name: "", module_name: "",
ident: "g", ident: "g",
suffixed: 0,
}, },
@5-6 Negate, @5-6 Negate,
), ),
@8-9 Var { @8-9 Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
], ],
Space, Space,
@ -50,6 +53,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-4 Identifier( @0-4 Identifier {
"main", ident: "main",
), suffixed: 0,
},
@11-71 SpaceBefore( @11-71 SpaceBefore(
Defs( Defs(
Defs { Defs {
@ -38,19 +39,22 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@11-15 Identifier( @11-15 Identifier {
"task", ident: "task",
), suffixed: 0,
},
@18-62 Backpassing( @18-62 Backpassing(
[ [
@18-22 Identifier( @18-22 Identifier {
"file", ident: "file",
), suffixed: 0,
},
], ],
@43-46 SpaceBefore( @43-46 SpaceBefore(
Var { Var {
module_name: "", module_name: "",
ident: "foo", ident: "foo",
suffixed: 0,
}, },
[ [
Newline, Newline,
@ -60,6 +64,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "bar", ident: "bar",
suffixed: 0,
}, },
[ [
Newline, Newline,
@ -73,6 +78,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "task", ident: "task",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -15,9 +15,10 @@ Defs {
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-4 Identifier( @0-4 Identifier {
"main", ident: "main",
), suffixed: 0,
},
@11-115 SpaceBefore( @11-115 SpaceBefore(
Defs( Defs(
Defs { Defs {
@ -37,9 +38,10 @@ Defs {
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@11-23 Identifier( @11-23 Identifier {
"wrappedNotEq", ident: "wrappedNotEq",
), suffixed: 0,
},
@26-38 Function( @26-38 Function(
[ [
@26-27 BoundVariable( @26-27 BoundVariable(
@ -57,9 +59,10 @@ Defs {
), ),
), ),
AnnotatedBody { AnnotatedBody {
ann_pattern: @11-23 Identifier( ann_pattern: @11-23 Identifier {
"wrappedNotEq", ident: "wrappedNotEq",
), suffixed: 0,
},
ann_type: @26-38 Function( ann_type: @26-38 Function(
[ [
@26-27 BoundVariable( @26-27 BoundVariable(
@ -76,17 +79,20 @@ Defs {
), ),
), ),
comment: None, comment: None,
body_pattern: @43-55 Identifier( body_pattern: @43-55 Identifier {
"wrappedNotEq", ident: "wrappedNotEq",
), suffixed: 0,
},
body_expr: @58-93 Closure( body_expr: @58-93 Closure(
[ [
@59-63 Identifier( @59-63 Identifier {
"num1", ident: "num1",
), suffixed: 0,
@65-69 Identifier( },
"num2", @65-69 Identifier {
), ident: "num2",
suffixed: 0,
},
], ],
@81-93 SpaceBefore( @81-93 SpaceBefore(
BinOps( BinOps(
@ -95,6 +101,7 @@ Defs {
@81-85 Var { @81-85 Var {
module_name: "", module_name: "",
ident: "num1", ident: "num1",
suffixed: 0,
}, },
@86-88 NotEquals, @86-88 NotEquals,
), ),
@ -102,6 +109,7 @@ Defs {
@89-93 Var { @89-93 Var {
module_name: "", module_name: "",
ident: "num2", ident: "num2",
suffixed: 0,
}, },
), ),
[ [
@ -117,6 +125,7 @@ Defs {
@99-111 Var { @99-111 Var {
module_name: "", module_name: "",
ident: "wrappedNotEq", ident: "wrappedNotEq",
suffixed: 0,
}, },
[ [
@112-113 Num( @112-113 Num(

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-1 Identifier( @0-1 Identifier {
"x", ident: "x",
), suffixed: 0,
},
@2-7 Defs( @2-7 Defs(
Defs { Defs {
tags: [ tags: [
@ -37,9 +38,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@2-3 Identifier( @2-3 Identifier {
"a", ident: "a",
), suffixed: 0,
},
@4-5 BoundVariable( @4-5 BoundVariable(
"n", "n",
), ),

View File

@ -4,6 +4,7 @@ If(
@3-5 Var { @3-5 Var {
module_name: "", module_name: "",
ident: "t1", ident: "t1",
suffixed: 0,
}, },
@13-14 SpaceBefore( @13-14 SpaceBefore(
SpaceAfter( SpaceAfter(
@ -23,6 +24,7 @@ If(
@23-25 Var { @23-25 Var {
module_name: "", module_name: "",
ident: "t2", ident: "t2",
suffixed: 0,
}, },
@33-34 SpaceBefore( @33-34 SpaceBefore(
SpaceAfter( SpaceAfter(

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-1 Identifier( @0-1 Identifier {
"x", ident: "x",
), suffixed: 0,
},
@8-9 SpaceBefore( @8-9 SpaceBefore(
Num( Num(
"5", "5",

View File

@ -16,9 +16,10 @@ Defs(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@0-1 Identifier( @0-1 Identifier {
"x", ident: "x",
), suffixed: 0,
},
@4-13 BinOps( @4-13 BinOps(
[ [
( (

View File

@ -100,14 +100,16 @@ Full {
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@199-203 Identifier( @199-203 Identifier {
"main", ident: "main",
), suffixed: 0,
},
@210-246 SpaceBefore( @210-246 SpaceBefore(
Apply( Apply(
@210-221 Var { @210-221 Var {
module_name: "Stdout", module_name: "Stdout",
ident: "line", ident: "line",
suffixed: 0,
}, },
[ [
@222-246 Str( @222-246 Str(

View File

@ -43,6 +43,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "p", ident: "p",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -32,6 +32,7 @@ Defs(
Var { Var {
module_name: "", module_name: "",
ident: "a", ident: "a",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -1,20 +1,23 @@
SpaceBefore( SpaceBefore(
Backpassing( Backpassing(
[ [
@18-19 Identifier( @18-19 Identifier {
"x", ident: "x",
), suffixed: 0,
},
], ],
@23-32 ParensAround( @23-32 ParensAround(
Closure( Closure(
[ [
@25-26 Identifier( @25-26 Identifier {
"y", ident: "y",
), suffixed: 0,
},
], ],
@30-31 Var { @30-31 Var {
module_name: "", module_name: "",
ident: "y", ident: "y",
suffixed: 0,
}, },
), ),
), ),
@ -22,6 +25,7 @@ SpaceBefore(
Var { Var {
module_name: "", module_name: "",
ident: "x", ident: "x",
suffixed: 0,
}, },
[ [
Newline, Newline,

View File

@ -17,9 +17,10 @@ SpaceBefore(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@18-19 Identifier( @18-19 Identifier {
"x", ident: "x",
), suffixed: 0,
},
@20-21 Num( @20-21 Num(
"5", "5",
), ),

View File

@ -17,9 +17,10 @@ SpaceBefore(
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@18-19 Identifier( @18-19 Identifier {
"x", ident: "x",
), suffixed: 0,
},
@22-23 Num( @22-23 Num(
"5", "5",
), ),

View File

@ -21,15 +21,17 @@ Defs(
"@Thunk", "@Thunk",
), ),
[ [
@7-9 Identifier( @7-9 Identifier {
"it", ident: "it",
), suffixed: 0,
},
], ],
), ),
@12-22 Apply( @12-22 Apply(
@12-14 Var { @12-14 Var {
module_name: "", module_name: "",
ident: "id", ident: "id",
suffixed: 0,
}, },
[ [
@16-21 ParensAround( @16-21 ParensAround(
@ -56,6 +58,7 @@ Defs(
@23-25 Var { @23-25 Var {
module_name: "", module_name: "",
ident: "it", ident: "it",
suffixed: 0,
}, },
[ [
@26-28 Record( @26-28 Record(

View File

@ -6,10 +6,12 @@ Apply(
@5-6 Var { @5-6 Var {
module_name: "", module_name: "",
ident: "m", ident: "m",
suffixed: 0,
}, },
@7-8 Var { @7-8 Var {
module_name: "", module_name: "",
ident: "n", ident: "n",
suffixed: 0,
}, },
], ],
Space, Space,

Some files were not shown because too many files have changed in this diff Show More