mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Merge branch 'main' into inline-imports
This commit is contained in:
commit
d5a38a26db
667 changed files with 22300 additions and 14562 deletions
|
@ -785,9 +785,10 @@ fn can_annotation_help(
|
|||
|
||||
for loc_var in *loc_vars {
|
||||
let var = match loc_var.value {
|
||||
Pattern::Identifier(name) if name.chars().next().unwrap().is_lowercase() => {
|
||||
name
|
||||
}
|
||||
Pattern::Identifier {
|
||||
ident: name,
|
||||
suffixed: _,
|
||||
} if name.chars().next().unwrap().is_lowercase() => name,
|
||||
_ => unreachable!("I thought this was validated during parsing"),
|
||||
};
|
||||
let var_name = Lowercase::from(var);
|
||||
|
|
|
@ -33,7 +33,6 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
|
|||
Symbol::NUM_TO_U32 => Some(lowlevel_1(Symbol::NUM_TO_U32, LowLevel::NumIntCast, var_store)),
|
||||
Symbol::NUM_TO_U64 => Some(lowlevel_1(Symbol::NUM_TO_U64, LowLevel::NumIntCast, var_store)),
|
||||
Symbol::NUM_TO_U128 => Some(lowlevel_1(Symbol::NUM_TO_U128, LowLevel::NumIntCast, var_store)),
|
||||
Symbol::NUM_TO_NAT => Some(lowlevel_1(Symbol::NUM_TO_NAT, LowLevel::NumIntCast, var_store)),
|
||||
|
||||
Symbol::NUM_INT_CAST => Some(lowlevel_1(Symbol::NUM_INT_CAST, LowLevel::NumIntCast, var_store)),
|
||||
|
||||
|
@ -50,7 +49,6 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
|
|||
Symbol::NUM_TO_U32_CHECKED => Some(to_num_checked(Symbol::NUM_TO_U32_CHECKED, var_store, LowLevel::NumToIntChecked)),
|
||||
Symbol::NUM_TO_U64_CHECKED => Some(to_num_checked(Symbol::NUM_TO_U64_CHECKED, var_store, LowLevel::NumToIntChecked)),
|
||||
Symbol::NUM_TO_U128_CHECKED => Some(to_num_checked(Symbol::NUM_TO_U128_CHECKED, var_store, LowLevel::NumToIntChecked)),
|
||||
Symbol::NUM_TO_NAT_CHECKED => Some(to_num_checked(Symbol::NUM_TO_NAT_CHECKED, var_store, LowLevel::NumToIntChecked)),
|
||||
|
||||
Symbol::NUM_TO_F32_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F32_CHECKED, var_store, LowLevel::NumToFloatChecked)),
|
||||
Symbol::NUM_TO_F64_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F64_CHECKED, var_store, LowLevel::NumToFloatChecked)),
|
||||
|
@ -118,7 +116,7 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
StrEndsWith; STR_ENDS_WITH; 2,
|
||||
StrSplit; STR_SPLIT; 2,
|
||||
StrCountUtf8Bytes; STR_COUNT_UTF8_BYTES; 1,
|
||||
StrFromUtf8Range; STR_FROM_UTF8_RANGE_LOWLEVEL; 3,
|
||||
StrFromUtf8; STR_FROM_UTF8_LOWLEVEL; 1,
|
||||
StrToUtf8; STR_TO_UTF8; 1,
|
||||
StrRepeat; STR_REPEAT; 2,
|
||||
StrTrim; STR_TRIM; 1,
|
||||
|
@ -128,11 +126,11 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
StrSubstringUnsafe; STR_SUBSTRING_UNSAFE; 3,
|
||||
StrReserve; STR_RESERVE; 2,
|
||||
StrToNum; STR_TO_NUM; 1,
|
||||
StrGetCapacity; STR_CAPACITY; 1,
|
||||
StrWithCapacity; STR_WITH_CAPACITY; 1,
|
||||
StrReleaseExcessCapacity; STR_RELEASE_EXCESS_CAPACITY; 1,
|
||||
|
||||
ListLen; LIST_LEN; 1,
|
||||
ListLenUsize; LIST_LEN_USIZE; 1,
|
||||
ListLenU64; LIST_LEN_U64; 1,
|
||||
ListWithCapacity; LIST_WITH_CAPACITY; 1,
|
||||
ListReserve; LIST_RESERVE; 2,
|
||||
ListIsUnique; LIST_IS_UNIQUE; 1,
|
||||
|
@ -173,9 +171,9 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
NumLte; NUM_LTE; 2,
|
||||
NumCompare; NUM_COMPARE; 2,
|
||||
NumDivFrac; NUM_DIV_FRAC; 2,
|
||||
NumDivTruncUnchecked; NUM_DIV_TRUNC; 2,
|
||||
NumDivTruncUnchecked; NUM_DIV_TRUNC_UNCHECKED; 2,
|
||||
NumDivCeilUnchecked; NUM_DIV_CEIL; 2,
|
||||
NumRemUnchecked; NUM_REM; 2,
|
||||
NumRemUnchecked; NUM_REM_UNCHECKED; 2,
|
||||
NumIsMultipleOf; NUM_IS_MULTIPLE_OF; 2,
|
||||
NumAbs; NUM_ABS; 1,
|
||||
NumNeg; NUM_NEG; 1,
|
||||
|
@ -196,10 +194,6 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
NumAtan; NUM_ATAN; 1,
|
||||
NumAcos; NUM_ACOS; 1,
|
||||
NumAsin; NUM_ASIN; 1,
|
||||
NumBytesToU16; NUM_BYTES_TO_U16_LOWLEVEL; 2,
|
||||
NumBytesToU32; NUM_BYTES_TO_U32_LOWLEVEL; 2,
|
||||
NumBytesToU64; NUM_BYTES_TO_U64_LOWLEVEL; 2,
|
||||
NumBytesToU128; NUM_BYTES_TO_U128_LOWLEVEL; 2,
|
||||
NumBitwiseAnd; NUM_BITWISE_AND; 2,
|
||||
NumBitwiseXor; NUM_BITWISE_XOR; 2,
|
||||
NumBitwiseOr; NUM_BITWISE_OR; 2,
|
||||
|
@ -210,7 +204,12 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
NumCountLeadingZeroBits; NUM_COUNT_LEADING_ZERO_BITS; 1,
|
||||
NumCountTrailingZeroBits; NUM_COUNT_TRAILING_ZERO_BITS; 1,
|
||||
NumCountOneBits; NUM_COUNT_ONE_BITS; 1,
|
||||
I128OfDec; I128_OF_DEC; 1,
|
||||
NumWithoutDecimalPoint; NUM_WITHOUT_DECIMAL_POINT; 1,
|
||||
NumWithDecimalPoint; NUM_WITH_DECIMAL_POINT; 1,
|
||||
NumF32ToParts; NUM_F32_TO_PARTS; 1,
|
||||
NumF64ToParts; NUM_F64_TO_PARTS; 1,
|
||||
NumF32FromParts; NUM_F32_FROM_PARTS; 1,
|
||||
NumF64FromParts; NUM_F64_FROM_PARTS; 1,
|
||||
|
||||
Eq; BOOL_STRUCTURAL_EQ; 2,
|
||||
NotEq; BOOL_STRUCTURAL_NOT_EQ; 2,
|
||||
|
@ -226,7 +225,7 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
/// Some builtins cannot be constructed in code gen alone, and need to be defined
|
||||
/// as separate Roc defs. For example, List.get has this type:
|
||||
///
|
||||
/// List.get : List elem, Nat -> Result elem [OutOfBounds]*
|
||||
/// List.get : List elem, U64 -> Result elem [OutOfBounds]*
|
||||
///
|
||||
/// Because this returns an open tag union for its Err type, it's not possible
|
||||
/// for code gen to return a hardcoded value for OutOfBounds. For example,
|
||||
|
|
|
@ -549,7 +549,11 @@ fn canonicalize_claimed_ability_impl<'a>(
|
|||
}
|
||||
AssignedField::RequiredValue(label, _spaces, value) => {
|
||||
let impl_ident = match value.value {
|
||||
ast::Expr::Var { module_name, ident } => {
|
||||
ast::Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: _,
|
||||
} => {
|
||||
if module_name.is_empty() {
|
||||
ident
|
||||
} else {
|
||||
|
@ -2644,9 +2648,10 @@ fn to_pending_alias_or_opaque<'a>(
|
|||
|
||||
for loc_var in vars.iter() {
|
||||
match loc_var.value {
|
||||
ast::Pattern::Identifier(name)
|
||||
if name.chars().next().unwrap().is_lowercase() =>
|
||||
{
|
||||
ast::Pattern::Identifier {
|
||||
ident: name,
|
||||
suffixed: _,
|
||||
} if name.chars().next().unwrap().is_lowercase() => {
|
||||
let lowercase = Lowercase::from(name);
|
||||
can_rigids.push(Loc {
|
||||
value: lowercase,
|
||||
|
@ -3069,7 +3074,13 @@ fn to_pending_value_def<'a>(
|
|||
let typed_ident = ingested_file.name.item.extract_spaces().item;
|
||||
let body_pattern = env
|
||||
.arena
|
||||
.alloc(typed_ident.ident.map_owned(ast::Pattern::Identifier));
|
||||
.alloc(typed_ident.ident.map_owned(|ident|
|
||||
ast::Pattern::Identifier {
|
||||
ident,
|
||||
// Agus TODO: check this
|
||||
suffixed: 0
|
||||
}
|
||||
));
|
||||
let ann_type = env.arena.alloc(typed_ident.ann);
|
||||
let body_expr = Loc {
|
||||
value: ast::Expr::IngestedFile(env.arena.alloc(file_path), ann_type),
|
||||
|
@ -3095,6 +3106,7 @@ fn to_pending_value_def<'a>(
|
|||
env.arena.alloc(body_expr),
|
||||
))
|
||||
}
|
||||
Stmt(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,13 @@ fn to_encoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
let opaque_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
|
||||
let opaque_apply_pattern = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
|
||||
// Encode.toEncoder payload
|
||||
|
@ -33,10 +37,12 @@ fn to_encoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Encode",
|
||||
ident: "toEncoder",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
})]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
));
|
||||
|
@ -61,19 +67,23 @@ fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "decodeWith",
|
||||
suffixed: 0,
|
||||
}),
|
||||
env.arena.alloc([
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: bytes,
|
||||
suffixed: 0,
|
||||
}),
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "decoder",
|
||||
suffixed: 0,
|
||||
}),
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: fmt,
|
||||
suffixed: 0,
|
||||
}),
|
||||
]),
|
||||
CalledVia::Space,
|
||||
|
@ -84,6 +94,7 @@ fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "mapResult",
|
||||
suffixed: 0,
|
||||
}),
|
||||
env.arena.alloc([
|
||||
&*alloc_expr(call_decode_with),
|
||||
|
@ -96,8 +107,20 @@ fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
// Decode.mapResult (Decode.decodeWith bytes Decode.decoder fmt) @Opaq
|
||||
let custom_closure = ast::Expr::Closure(
|
||||
env.arena.alloc([
|
||||
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(bytes)),
|
||||
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(fmt)),
|
||||
Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: bytes,
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: fmt,
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
]),
|
||||
alloc_expr(call_map_result),
|
||||
);
|
||||
|
@ -107,6 +130,7 @@ fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "custom",
|
||||
suffixed: 0,
|
||||
}),
|
||||
env.arena.alloc([&*alloc_expr(custom_closure)]),
|
||||
CalledVia::Space,
|
||||
|
@ -127,9 +151,13 @@ fn hash<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
let opaque_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
|
||||
let opaque_apply_pattern = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
|
||||
// Hash.hash hasher payload
|
||||
|
@ -137,15 +165,18 @@ fn hash<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Hash",
|
||||
ident: "hash",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: hasher,
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
}),
|
||||
]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
|
@ -154,7 +185,13 @@ fn hash<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
// \hasher, @Opaq payload -> Hash.hash hasher payload
|
||||
ast::Expr::Closure(
|
||||
env.arena.alloc([
|
||||
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(hasher)),
|
||||
Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: hasher,
|
||||
suffixed: 0,
|
||||
},
|
||||
),
|
||||
Loc::at(DERIVED_REGION, opaque_apply_pattern),
|
||||
]),
|
||||
call_member,
|
||||
|
@ -172,16 +209,24 @@ fn is_eq<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
// \@Opaq payload1
|
||||
let opaque1 = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload1))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload1,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
// \@Opaq payload2
|
||||
let opaque2 = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload2))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload2,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
|
||||
// Bool.isEq payload1 payload2
|
||||
|
@ -189,15 +234,18 @@ fn is_eq<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Bool",
|
||||
ident: "isEq",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload1,
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload2,
|
||||
suffixed: 0,
|
||||
}),
|
||||
]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
|
@ -224,9 +272,13 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
let opaque_ref = alloc_pat(ast::Pattern::OpaqueRef(at_opaque));
|
||||
let opaque_apply_pattern = ast::Pattern::Apply(
|
||||
opaque_ref,
|
||||
&*env
|
||||
.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(payload))]),
|
||||
&*env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
);
|
||||
|
||||
// Inspect.toInspector payload
|
||||
|
@ -234,10 +286,12 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Inspect",
|
||||
ident: "toInspector",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: payload,
|
||||
suffixed: 0,
|
||||
})]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
));
|
||||
|
@ -252,6 +306,7 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Inspect",
|
||||
ident: "tag",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([&*opaque_name, &*to_inspector_list]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
|
@ -264,20 +319,27 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Inspect",
|
||||
ident: "apply",
|
||||
suffixed: 0,
|
||||
}),
|
||||
&*env.arena.alloc([
|
||||
&*opaque_inspector,
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: fmt,
|
||||
suffixed: 0,
|
||||
}),
|
||||
]),
|
||||
roc_module::called_via::CalledVia::Space,
|
||||
));
|
||||
|
||||
let custom_closure = alloc_expr(ast::Expr::Closure(
|
||||
env.arena
|
||||
.alloc([Loc::at(DERIVED_REGION, ast::Pattern::Identifier(fmt))]),
|
||||
env.arena.alloc([Loc::at(
|
||||
DERIVED_REGION,
|
||||
ast::Pattern::Identifier {
|
||||
ident: fmt,
|
||||
suffixed: 0,
|
||||
},
|
||||
)]),
|
||||
apply_opaque_inspector,
|
||||
));
|
||||
|
||||
|
@ -286,6 +348,7 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Inspect",
|
||||
ident: "custom",
|
||||
suffixed: 0,
|
||||
}),
|
||||
env.arena.alloc([&*custom_closure]),
|
||||
CalledVia::Space,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(clippy::manual_map)]
|
||||
|
||||
use crate::suffixed::{apply_task_await, unwrap_suffixed_expression, EUnwrapped};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_error_macros::internal_error;
|
||||
|
@ -8,8 +9,8 @@ use roc_module::called_via::{BinOp, CalledVia};
|
|||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Collection, Pattern, RecordBuilderField, StrLiteral, StrSegment, ValueDef,
|
||||
WhenBranch,
|
||||
wrap_in_task_ok, AssignedField, Collection, Pattern, RecordBuilderField, StrLiteral,
|
||||
StrSegment, ValueDef, WhenBranch,
|
||||
};
|
||||
use roc_region::all::{LineInfo, Loc, Region};
|
||||
|
||||
|
@ -56,7 +57,11 @@ fn new_op_call_expr<'a>(
|
|||
let args = arena.alloc([left, right]);
|
||||
|
||||
let loc_expr = arena.alloc(Loc {
|
||||
value: Expr::Var { module_name, ident },
|
||||
value: Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: 0,
|
||||
},
|
||||
region: loc_op.region,
|
||||
});
|
||||
|
||||
|
@ -92,9 +97,10 @@ fn desugar_value_def<'a>(
|
|||
ann_pattern,
|
||||
ann_type,
|
||||
comment: *comment,
|
||||
body_pattern,
|
||||
body_pattern: desugar_loc_pattern(arena, body_pattern, src, line_info, module_path),
|
||||
body_expr: desugar_expr(arena, body_expr, src, line_info, module_path),
|
||||
},
|
||||
|
||||
Dbg {
|
||||
condition,
|
||||
preceding_comment,
|
||||
|
@ -135,19 +141,137 @@ fn desugar_value_def<'a>(
|
|||
exposed: _,
|
||||
}) => *def,
|
||||
IngestedFileImport(_) => *def,
|
||||
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn desugar_defs<'a>(
|
||||
pub fn desugar_defs_node_values<'a>(
|
||||
arena: &'a Bump,
|
||||
defs: &mut roc_parse::ast::Defs<'a>,
|
||||
src: &'a str,
|
||||
line_info: &mut Option<LineInfo>,
|
||||
module_path: &str,
|
||||
top_level_def: bool,
|
||||
) {
|
||||
for value_def in defs.value_defs.iter_mut() {
|
||||
*value_def = desugar_value_def(arena, arena.alloc(*value_def), src, line_info, module_path);
|
||||
}
|
||||
|
||||
// `desugar_defs_node_values` is called recursively in `desugar_expr` and we
|
||||
// only we only want to unwrap suffixed nodes if they are a top level def.
|
||||
//
|
||||
// check here first so we only unwrap the expressions once, and after they have
|
||||
// been desugared
|
||||
if top_level_def {
|
||||
for value_def in defs.value_defs.iter_mut() {
|
||||
*value_def = desugar_value_def_suffixed(arena, *value_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For each top-level ValueDef in our module, we will unwrap any suffixed
|
||||
/// expressions
|
||||
///
|
||||
/// e.g. `say! "hi"` desugars to `Task.await (say "hi") -> \{} -> ...`
|
||||
pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) -> ValueDef<'a> {
|
||||
use ValueDef::*;
|
||||
|
||||
match value_def {
|
||||
Body(loc_pattern, loc_expr) => {
|
||||
// note called_from_def is passed as `false` as this is a top_level_def
|
||||
match unwrap_suffixed_expression(arena, loc_expr, None) {
|
||||
Ok(new_expr) => Body(loc_pattern, new_expr),
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
}) => desugar_value_def_suffixed(
|
||||
arena,
|
||||
Body(
|
||||
loc_pattern,
|
||||
apply_task_await(
|
||||
arena,
|
||||
loc_expr.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
wrap_in_task_ok(arena, sub_new),
|
||||
),
|
||||
),
|
||||
),
|
||||
Err(..) => Body(
|
||||
loc_pattern,
|
||||
arena.alloc(Loc::at(loc_expr.region, MalformedSuffixed(loc_expr))),
|
||||
),
|
||||
}
|
||||
}
|
||||
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,
|
||||
}) => desugar_value_def_suffixed(
|
||||
arena,
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment,
|
||||
body_pattern,
|
||||
body_expr: apply_task_await(
|
||||
arena,
|
||||
body_expr.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
wrap_in_task_ok(arena, 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,
|
||||
ModuleImport { .. } | IngestedFileImport(_) => value_def,
|
||||
|
||||
Stmt(..) => {
|
||||
internal_error!(
|
||||
"this should have been desugared into a Body(..) before this call in desugar_expr"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reorder the expression tree based on operator precedence and associativity rules,
|
||||
|
@ -169,6 +293,7 @@ pub fn desugar_expr<'a>(
|
|||
| Underscore { .. }
|
||||
| MalformedIdent(_, _)
|
||||
| MalformedClosure
|
||||
| MalformedSuffixed(..)
|
||||
| PrecedenceConflict { .. }
|
||||
| MultipleRecordBuilders { .. }
|
||||
| UnappliedRecordBuilder { .. }
|
||||
|
@ -370,8 +495,7 @@ pub fn desugar_expr<'a>(
|
|||
),
|
||||
Defs(defs, loc_ret) => {
|
||||
let mut defs = (*defs).clone();
|
||||
desugar_defs(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);
|
||||
|
||||
arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret)))
|
||||
|
@ -485,10 +609,12 @@ pub fn desugar_expr<'a>(
|
|||
Negate => Var {
|
||||
module_name: ModuleName::NUM,
|
||||
ident: "neg",
|
||||
suffixed: 0,
|
||||
},
|
||||
Not => Var {
|
||||
module_name: ModuleName::BOOL,
|
||||
ident: "not",
|
||||
suffixed: 0,
|
||||
},
|
||||
};
|
||||
let loc_fn_var = arena.alloc(Loc { region, value });
|
||||
|
@ -586,6 +712,7 @@ pub fn desugar_expr<'a>(
|
|||
let inspect_fn = Var {
|
||||
module_name: ModuleName::INSPECT,
|
||||
ident: "toStr",
|
||||
suffixed: 0,
|
||||
};
|
||||
let loc_inspect_fn_var = arena.alloc(Loc {
|
||||
value: inspect_fn,
|
||||
|
@ -601,7 +728,7 @@ pub fn desugar_expr<'a>(
|
|||
|
||||
// line_info is an option so that we can lazily calculate it.
|
||||
// That way it there are no `dbg` statements, we never pay the cast of scanning the source an extra time.
|
||||
if matches!(line_info, None) {
|
||||
if line_info.is_none() {
|
||||
*line_info = Some(LineInfo::new(src));
|
||||
}
|
||||
let line_col = line_info.as_ref().unwrap().convert_pos(region.start());
|
||||
|
@ -625,7 +752,32 @@ pub fn desugar_expr<'a>(
|
|||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
LowLevelDbg(_, _, _) => unreachable!("Only exists after desugaring"),
|
||||
|
||||
// Replace an empty final def with a `Task.ok {}`
|
||||
EmptyDefsFinal => {
|
||||
let mut apply_args: Vec<&'a Loc<Expr<'a>>> = Vec::new_in(arena);
|
||||
apply_args
|
||||
.push(arena.alloc(Loc::at(loc_expr.region, Expr::Record(Collection::empty()))));
|
||||
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Apply(
|
||||
arena.alloc(Loc::at(
|
||||
loc_expr.region,
|
||||
Expr::Var {
|
||||
module_name: ModuleName::TASK,
|
||||
ident: "ok",
|
||||
suffixed: 0,
|
||||
},
|
||||
)),
|
||||
arena.alloc(apply_args),
|
||||
CalledVia::BangSuffix,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// note this only exists after desugaring
|
||||
LowLevelDbg(_, _, _) => loc_expr,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,6 +863,7 @@ fn desugar_field<'a>(
|
|||
value: Var {
|
||||
module_name: "",
|
||||
ident: loc_str.value,
|
||||
suffixed: 0,
|
||||
},
|
||||
region: loc_str.region,
|
||||
};
|
||||
|
@ -771,7 +924,7 @@ fn desugar_pattern<'a>(
|
|||
use roc_parse::ast::Pattern::*;
|
||||
|
||||
match pattern {
|
||||
Identifier(_)
|
||||
Identifier { .. }
|
||||
| Tag(_)
|
||||
| OpaqueRef(_)
|
||||
| NumLiteral(_)
|
||||
|
@ -891,6 +1044,7 @@ fn record_builder_arg<'a>(
|
|||
value: Expr::Var {
|
||||
module_name: "",
|
||||
ident: arena.alloc("#".to_owned() + label.value),
|
||||
suffixed: 0,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -930,7 +1084,10 @@ fn record_builder_arg<'a>(
|
|||
|
||||
for label in apply_field_names.iter().rev() {
|
||||
let name = arena.alloc("#".to_owned() + label.value);
|
||||
let ident = roc_parse::ast::Pattern::Identifier(name);
|
||||
let ident = roc_parse::ast::Pattern::Identifier {
|
||||
ident: name,
|
||||
suffixed: 0,
|
||||
};
|
||||
|
||||
let arg_pattern = arena.alloc(Loc {
|
||||
value: ident,
|
|
@ -624,6 +624,9 @@ pub fn canonicalize_expr<'a>(
|
|||
use Expr::*;
|
||||
|
||||
let (expr, output) = match expr {
|
||||
&ast::Expr::EmptyDefsFinal => {
|
||||
internal_error!("EmptyDefsFinal should have been desugared")
|
||||
}
|
||||
&ast::Expr::Num(str) => {
|
||||
let answer = num_expr_from_result(var_store, finish_parsing_num(str), region, env);
|
||||
|
||||
|
@ -1051,9 +1054,11 @@ pub fn canonicalize_expr<'a>(
|
|||
(expr, output)
|
||||
}
|
||||
}
|
||||
ast::Expr::Var { module_name, ident } => {
|
||||
canonicalize_var_lookup(env, var_store, scope, module_name, ident, region)
|
||||
}
|
||||
ast::Expr::Var {
|
||||
module_name,
|
||||
ident,
|
||||
suffixed: _, // TODO should we use suffixed here?
|
||||
} => canonicalize_var_lookup(env, var_store, scope, module_name, ident, region),
|
||||
ast::Expr::Underscore(name) => {
|
||||
// we parse underscores, but they are not valid expression syntax
|
||||
|
||||
|
@ -1457,6 +1462,10 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
(RuntimeError(problem), Output::default())
|
||||
}
|
||||
ast::Expr::MalformedSuffixed(..) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
(RuntimeError(MalformedSuffixed(region)), Output::default())
|
||||
}
|
||||
ast::Expr::MultipleRecordBuilders(sub_expr) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
|
@ -1685,7 +1694,8 @@ fn canonicalize_closure_body<'a>(
|
|||
enum MultiPatternVariables {
|
||||
OnePattern,
|
||||
MultiPattern {
|
||||
bound_occurrences: VecMap<Symbol, (Region, u8)>,
|
||||
num_patterns: usize,
|
||||
bound_occurrences: VecMap<Symbol, (Region, usize)>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1694,7 +1704,8 @@ impl MultiPatternVariables {
|
|||
fn new(num_patterns: usize) -> Self {
|
||||
if num_patterns > 1 {
|
||||
Self::MultiPattern {
|
||||
bound_occurrences: VecMap::with_capacity(2),
|
||||
num_patterns,
|
||||
bound_occurrences: VecMap::with_capacity(num_patterns),
|
||||
}
|
||||
} else {
|
||||
Self::OnePattern
|
||||
|
@ -1705,7 +1716,9 @@ impl MultiPatternVariables {
|
|||
fn add_pattern(&mut self, pattern: &Loc<Pattern>) {
|
||||
match self {
|
||||
MultiPatternVariables::OnePattern => {}
|
||||
MultiPatternVariables::MultiPattern { bound_occurrences } => {
|
||||
MultiPatternVariables::MultiPattern {
|
||||
bound_occurrences, ..
|
||||
} => {
|
||||
for (sym, region) in BindingsFromPattern::new(pattern) {
|
||||
if !bound_occurrences.contains_key(&sym) {
|
||||
bound_occurrences.insert(sym, (region, 0));
|
||||
|
@ -1718,15 +1731,18 @@ impl MultiPatternVariables {
|
|||
|
||||
#[inline(always)]
|
||||
fn get_unbound(self) -> impl Iterator<Item = (Symbol, Region)> {
|
||||
let bound_occurrences = match self {
|
||||
MultiPatternVariables::OnePattern => Default::default(),
|
||||
MultiPatternVariables::MultiPattern { bound_occurrences } => bound_occurrences,
|
||||
let (bound_occurrences, num_patterns) = match self {
|
||||
MultiPatternVariables::OnePattern => (Default::default(), 1),
|
||||
MultiPatternVariables::MultiPattern {
|
||||
bound_occurrences,
|
||||
num_patterns,
|
||||
} => (bound_occurrences, num_patterns),
|
||||
};
|
||||
|
||||
bound_occurrences
|
||||
.into_iter()
|
||||
.filter_map(|(sym, (region, occurs))| {
|
||||
if occurs == 1 {
|
||||
.filter_map(move |(sym, (region, occurs))| {
|
||||
if occurs != num_patterns {
|
||||
Some((sym, region))
|
||||
} else {
|
||||
None
|
||||
|
@ -2524,7 +2540,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::Expr::IngestedFile(_, _)
|
||||
| ast::Expr::SpaceBefore(_, _)
|
||||
| ast::Expr::Str(StrLiteral::Block(_))
|
||||
| ast::Expr::SpaceAfter(_, _) => false,
|
||||
| ast::Expr::SpaceAfter(_, _)
|
||||
| ast::Expr::EmptyDefsFinal => false,
|
||||
// These can contain subexpressions, so we need to recursively check those
|
||||
ast::Expr::Str(StrLiteral::Line(segments)) => {
|
||||
segments.iter().all(|segment| match segment {
|
||||
|
@ -2550,6 +2567,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
.iter()
|
||||
.all(|loc_field| is_valid_interpolation(&loc_field.value)),
|
||||
ast::Expr::MultipleRecordBuilders(loc_expr)
|
||||
| ast::Expr::MalformedSuffixed(loc_expr)
|
||||
| ast::Expr::UnappliedRecordBuilder(loc_expr)
|
||||
| ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. })
|
||||
| ast::Expr::UnaryOp(loc_expr, _)
|
||||
|
@ -2712,10 +2730,38 @@ fn flatten_str_lines<'a>(
|
|||
fn desugar_str_segments(var_store: &mut VarStore, segments: Vec<StrSegment>) -> Expr {
|
||||
use StrSegment::*;
|
||||
|
||||
let n = segments.len();
|
||||
let mut iter = segments.into_iter().rev();
|
||||
let mut loc_expr = match iter.next() {
|
||||
Some(Plaintext(string)) => Loc::at(Region::zero(), Expr::Str(string)),
|
||||
Some(Interpolation(loc_expr)) => loc_expr,
|
||||
Some(Interpolation(loc_expr)) => {
|
||||
if n == 1 {
|
||||
// We concat with the empty string to ensure a type error when loc_expr is not a string
|
||||
let empty_string = Loc::at(Region::zero(), Expr::Str("".into()));
|
||||
|
||||
let fn_expr = Loc::at(
|
||||
Region::zero(),
|
||||
Expr::Var(Symbol::STR_CONCAT, var_store.fresh()),
|
||||
);
|
||||
let expr = Expr::Call(
|
||||
Box::new((
|
||||
var_store.fresh(),
|
||||
fn_expr,
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
)),
|
||||
vec![
|
||||
(var_store.fresh(), empty_string),
|
||||
(var_store.fresh(), loc_expr),
|
||||
],
|
||||
CalledVia::StringInterpolation,
|
||||
);
|
||||
|
||||
Loc::at(Region::zero(), expr)
|
||||
} else {
|
||||
loc_expr
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// No segments? Empty string!
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ pub mod constraint;
|
|||
pub mod copy;
|
||||
pub mod def;
|
||||
mod derive;
|
||||
pub mod desugar;
|
||||
pub mod effect_module;
|
||||
pub mod env;
|
||||
pub mod exhaustive;
|
||||
|
@ -20,11 +21,11 @@ pub mod expected;
|
|||
pub mod expr;
|
||||
pub mod module;
|
||||
pub mod num;
|
||||
pub mod operator;
|
||||
pub mod pattern;
|
||||
pub mod procedure;
|
||||
pub mod scope;
|
||||
pub mod string;
|
||||
pub mod suffixed;
|
||||
pub mod traverse;
|
||||
|
||||
pub use derive::DERIVED_REGION;
|
||||
|
|
|
@ -310,7 +310,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
// visited a BinOp node we'd recursively try to apply this to each of its nested
|
||||
// operators, and then again on *their* nested operators, ultimately applying the
|
||||
// rules multiple times unnecessarily.
|
||||
crate::operator::desugar_defs(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();
|
||||
|
||||
|
@ -1041,14 +1042,13 @@ fn fix_values_captured_in_closure_expr(
|
|||
debug_assert!(!captures.is_empty());
|
||||
captured_symbols.extend(captures);
|
||||
captured_symbols.swap_remove(i);
|
||||
// Jump two, because the next element is now one of the newly-added captures,
|
||||
// which we don't need to check.
|
||||
i += 2;
|
||||
|
||||
added_captures = true;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Always jump one, because the current element either does not have captures or
|
||||
// is now one of the newly-added captures, which we don't need to check.
|
||||
i += 1;
|
||||
}
|
||||
if added_captures {
|
||||
// Re-sort, since we've added new captures.
|
||||
|
|
|
@ -198,7 +198,6 @@ fn parse_literal_suffix(num_str: &str) -> (Option<ParsedWidth>, &str) {
|
|||
"i32", ParsedWidth::Int(IntLitWidth::I32)
|
||||
"i64", ParsedWidth::Int(IntLitWidth::I64)
|
||||
"i128", ParsedWidth::Int(IntLitWidth::I128)
|
||||
"nat", ParsedWidth::Int(IntLitWidth::Nat)
|
||||
"dec", ParsedWidth::Float(FloatWidth::Dec)
|
||||
"f32", ParsedWidth::Float(FloatWidth::F32)
|
||||
"f64", ParsedWidth::Float(FloatWidth::F64)
|
||||
|
|
|
@ -266,7 +266,10 @@ pub fn canonicalize_def_header_pattern<'a>(
|
|||
|
||||
match pattern {
|
||||
// Identifiers that shadow ability members may appear (and may only appear) at the header of a def.
|
||||
Identifier(name) => {
|
||||
Identifier {
|
||||
ident: name,
|
||||
suffixed: _,
|
||||
} => {
|
||||
match scope.introduce_or_shadow_ability_member(
|
||||
pending_abilities_in_scope,
|
||||
(*name).into(),
|
||||
|
@ -376,12 +379,13 @@ pub fn canonicalize_pattern<'a>(
|
|||
use PatternType::*;
|
||||
|
||||
let can_pattern = match pattern {
|
||||
Identifier(name) => {
|
||||
match canonicalize_pattern_symbol(env, scope, output, region, permit_shadows, name) {
|
||||
Ok(symbol) => Pattern::Identifier(symbol),
|
||||
Err(pattern) => pattern,
|
||||
}
|
||||
}
|
||||
Identifier {
|
||||
ident: name,
|
||||
suffixed: _,
|
||||
} => match canonicalize_pattern_symbol(env, scope, output, region, permit_shadows, name) {
|
||||
Ok(symbol) => Pattern::Identifier(symbol),
|
||||
Err(pattern) => pattern,
|
||||
},
|
||||
Underscore(name) => {
|
||||
// An underscored identifier can't be used, but we'll still add it to the scope
|
||||
// for better error messages if someone tries to use it.
|
||||
|
@ -637,7 +641,10 @@ pub fn canonicalize_pattern<'a>(
|
|||
|
||||
for loc_pattern in patterns.iter() {
|
||||
match loc_pattern.value {
|
||||
Identifier(label) => {
|
||||
Identifier {
|
||||
ident: label,
|
||||
suffixed: _,
|
||||
} => {
|
||||
match scope.introduce(label.into(), region) {
|
||||
Ok(symbol) => {
|
||||
output.references.insert_bound(symbol);
|
||||
|
|
|
@ -45,7 +45,7 @@ pub fn canonical_string_literal<'a>(_arena: &Bump, _raw: &'a str, _region: Regio
|
|||
// )? {
|
||||
// let expr = Expr::Var(ident);
|
||||
|
||||
// // +2 for `\(` and then another +1 for `)` at the end
|
||||
// // +2 for `$(` and then another +1 for `)` at the end
|
||||
// let parsed_length = buf.len() + 2 + ident.len() + 1;
|
||||
|
||||
// // Casting should always succeed in this section, because
|
||||
|
|
881
crates/compiler/can/src/suffixed.rs
Normal file
881
crates/compiler/can/src/suffixed.rs
Normal file
|
@ -0,0 +1,881 @@
|
|||
#![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, wrap_in_task_ok, Pattern, ValueDef, WhenBranch};
|
||||
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
|
||||
// if is_loc_expr_suffixed(loc_expr) {
|
||||
// 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 unwrapped_expression = apply_task_await(
|
||||
arena,
|
||||
sub_arg.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
wrap_in_task_ok(arena, sub_new),
|
||||
);
|
||||
|
||||
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 unwrapped_final_else = apply_task_await(
|
||||
arena,
|
||||
sub_arg.region,
|
||||
sub_arg,
|
||||
sub_pat,
|
||||
wrap_in_task_ok(arena, sub_new),
|
||||
);
|
||||
|
||||
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>> {
|
||||
match loc_expr.value {
|
||||
Expr::When(condition, branches) => {
|
||||
|
||||
// first unwrap any when branches values
|
||||
// e.g.
|
||||
// when foo is
|
||||
// [] -> line! "bar"
|
||||
// _ -> line! "baz"
|
||||
for (branch_index, WhenBranch{value: branch_loc_expr,patterns, guard}) in branches.iter().enumerate() {
|
||||
|
||||
// if the branch isn't suffixed we can leave it alone
|
||||
if is_loc_expr_suffixed(branch_loc_expr) {
|
||||
let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) {
|
||||
Ok(unwrapped_branch_value) => unwrapped_branch_value,
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new),
|
||||
Err(..) => return Err(EUnwrapped::Malformed),
|
||||
};
|
||||
|
||||
let new_branch = WhenBranch{value: *unwrapped_branch_value, patterns, guard: *guard};
|
||||
let mut new_branches = Vec::new_in(arena);
|
||||
let (before, rest) = branches.split_at(branch_index);
|
||||
let after = &rest[1..];
|
||||
|
||||
new_branches.extend_from_slice(before);
|
||||
new_branches.push(arena.alloc(new_branch));
|
||||
new_branches.extend_from_slice(after);
|
||||
|
||||
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(condition, arena.alloc_slice_copy(new_branches.as_slice()))));
|
||||
|
||||
return unwrap_suffixed_expression(arena, new_when, maybe_def_pat);
|
||||
}
|
||||
}
|
||||
|
||||
// then unwrap the when condition
|
||||
match unwrap_suffixed_expression(arena, condition, None) {
|
||||
Ok(unwrapped_condition) => {
|
||||
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches)));
|
||||
Ok(new_when)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => {
|
||||
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches)));
|
||||
let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when);
|
||||
Ok(applied_task_await)
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr(..))
|
||||
| Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed)
|
||||
}
|
||||
|
||||
}
|
||||
_ => internal_error!("unreachable, expected a When node to be passed into unwrap_suffixed_expression_defs_help"),
|
||||
}
|
||||
}
|
||||
|
||||
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(..) | ModuleImport{..} | IngestedFileImport(_) => 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 }) => {
|
||||
// We need to apply Task.ok here as the defs final expression was unwrapped
|
||||
apply_task_await(arena,def_expr.region,sub_arg,sub_pat,wrap_in_task_ok(arena, 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_matching_empty_record(loc_pat, loc_new) {
|
||||
return loc_arg;
|
||||
}
|
||||
|
||||
// If the pattern and the new are matching answers then we don't need to unwrap anything
|
||||
// e.g. `Task.await foo \#!a1 -> Task.ok #!a1` is the same as `foo`
|
||||
if is_matching_intermediate_answer(loc_pat, 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 extract_wrapped_task_ok_value<'a>(loc_expr: &'a Loc<Expr<'a>>) -> Option<&'a Loc<Expr<'a>>> {
|
||||
match loc_expr.value {
|
||||
Expr::Apply(function, arguments, _) => match function.value {
|
||||
Var {
|
||||
module_name, ident, ..
|
||||
} if module_name == ModuleName::TASK && ident == "ok" => arguments.first().copied(),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_matching_empty_record<'a>(
|
||||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
) -> bool {
|
||||
let is_empty_record = match extract_wrapped_task_ok_value(loc_expr) {
|
||||
Some(task_expr) => match task_expr.value {
|
||||
Expr::Record(collection) => collection.is_empty(),
|
||||
_ => false,
|
||||
},
|
||||
None => false,
|
||||
};
|
||||
|
||||
let is_pattern_empty_record = match loc_pat.value {
|
||||
Pattern::RecordDestructure(collection) => collection.is_empty(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
is_empty_record && is_pattern_empty_record
|
||||
}
|
||||
|
||||
fn is_matching_intermediate_answer<'a>(
|
||||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
) -> bool {
|
||||
let pat_ident = match loc_pat.value {
|
||||
Pattern::Identifier { ident, .. } => Some(ident),
|
||||
_ => None,
|
||||
};
|
||||
let exp_ident = match extract_wrapped_task_ok_value(loc_expr) {
|
||||
Some(task_expr) => match task_expr.value {
|
||||
Expr::Var {
|
||||
module_name, ident, ..
|
||||
} if module_name.is_empty() && ident.starts_with('#') => Some(ident),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
match (pat_ident, exp_ident) {
|
||||
(Some(a), Some(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
},
|
||||
pattern::{DestructType, Pattern, RecordDestruct, TupleDestruct},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum DeclarationInfo<'a> {
|
||||
Value {
|
||||
loc_symbol: Loc<Symbol>,
|
||||
|
@ -164,7 +164,7 @@ pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
|
|||
}
|
||||
}
|
||||
|
||||
fn walk_decl<V: Visitor>(visitor: &mut V, decl: DeclarationInfo<'_>) {
|
||||
pub fn walk_decl<V: Visitor>(visitor: &mut V, decl: DeclarationInfo<'_>) {
|
||||
use DeclarationInfo::*;
|
||||
|
||||
match decl {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue