implement optional fields in function pattern matches

This commit is contained in:
Folkert 2020-08-13 00:09:57 +02:00
parent ab78725944
commit 20ddbeb528
5 changed files with 264 additions and 52 deletions

View file

@ -1399,13 +1399,20 @@ pub fn with_hole<'a>(
for (label, layout) in sorted_fields.into_iter() {
field_layouts.push(layout);
let field = fields.remove(&label).unwrap();
if let roc_can::expr::Expr::Var(symbol) = field.loc_expr.value {
field_symbols.push(symbol);
can_fields.push(None);
} else {
field_symbols.push(env.unique_symbol());
can_fields.push(Some(field));
match fields.remove(&label) {
Some(field) => {
if let roc_can::expr::Expr::Var(symbol) = field.loc_expr.value {
field_symbols.push(symbol);
can_fields.push(None);
} else {
field_symbols.push(env.unique_symbol());
can_fields.push(Some(field));
}
}
None => {
// this field was optional, but not given
continue;
}
}
}
@ -1643,11 +1650,22 @@ pub fn with_hole<'a>(
let mut index = None;
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
for (current, (label, field_layout)) in sorted_fields.into_iter().enumerate() {
field_layouts.push(field_layout);
let mut current = 0;
for (label, opt_field_layout) in sorted_fields.into_iter() {
match opt_field_layout {
Err(_) => {
// this was an optional field, and now does not exist!
// do not increment `current`!
}
Ok(field_layout) => {
field_layouts.push(field_layout);
if label == field {
index = Some(current);
if label == field {
index = Some(current);
}
current += 1;
}
}
}
@ -2670,8 +2688,15 @@ fn store_record_destruct<'a>(
env.arena.alloc(stmt),
);
}
DestructType::Optional(_expr) => {
todo!("TODO monomorphize optional field destructure's default expr");
DestructType::Optional(expr) => {
stmt = with_hole(
env,
expr.clone(),
procs,
layout_cache,
destruct.symbol,
env.arena.alloc(stmt),
);
}
DestructType::Guard(guard_pattern) => match &guard_pattern {
Identifier(symbol) => {
@ -3173,6 +3198,7 @@ pub fn from_can_pattern<'a>(
destructs,
..
} => {
// sorted fields based on the destruct
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
let mut destructs = destructs.clone();
destructs.sort_by(|a, b| a.value.label.cmp(&b.value.label));
@ -3180,40 +3206,100 @@ pub fn from_can_pattern<'a>(
let mut it = destructs.iter();
let mut opt_destruct = it.next();
// sorted fields based on the type
let sorted_fields = crate::layout::sort_record_fields(env.arena, *whole_var, env.subs);
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
for (label, field_layout) in sorted_fields.into_iter() {
if let Some(destruct) = opt_destruct {
if destruct.value.label == label {
opt_destruct = it.next();
// next we step through both sequences of fields. The outer loop is the sequence based
// on the type, since not all fields need to actually be destructured in the source
// language.
//
// However in mono patterns, we do destruct all patterns (but use Underscore) when
// in the source the field is not matche in the source language.
//
// Optional fields somewhat complicate the matter here
for (label, opt_field_layout) in sorted_fields.into_iter() {
match opt_field_layout {
Ok(field_layout) => {
match opt_destruct {
Some(destruct) => {
if destruct.value.label == label {
opt_destruct = it.next();
mono_destructs.push(from_can_record_destruct(
env,
layout_cache,
&destruct.value,
field_layout.clone(),
));
} else {
// insert underscore pattern
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
mono_destructs.push(from_can_record_destruct(
env,
layout_cache,
&destruct.value,
field_layout.clone(),
));
} else {
// insert underscore pattern
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
}
}
None => {
// the remainder of the fields (from the type) is not matched on in
// this pattern; to fill it out, we put underscores
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
}
}
field_layouts.push(field_layout);
}
Err(field_layout) => {
// this field was optional, and now does not exist
// if it was actually matched on, we need to evaluate the default
match opt_destruct {
Some(destruct) => {
if destruct.value.label == label {
opt_destruct = it.next();
mono_destructs.push(RecordDestruct {
label: destruct.value.label.clone(),
symbol: destruct.value.symbol,
layout: field_layout,
typ: match &destruct.value.typ {
roc_can::pattern::DestructType::Optional(
_,
loc_expr,
) => {
// if we reach this stage, the optional field is not present
// so use the default
DestructType::Optional(loc_expr.value.clone())
}
_ => unreachable!(
"only optional destructs can be optional fields"
),
},
});
} else {
// insert underscore pattern
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
}
}
None => {
// this field does not exist, and was not request in the pattern match
continue;
}
}
}
} else {
// insert underscore pattern
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
}
field_layouts.push(field_layout);
}
Pattern::RecordDestructure(
@ -3236,8 +3322,10 @@ fn from_can_record_destruct<'a>(
layout: field_layout,
typ: match &can_rd.typ {
roc_can::pattern::DestructType::Required => DestructType::Required,
roc_can::pattern::DestructType::Optional(_, loc_expr) => {
DestructType::Optional(loc_expr.value.clone())
roc_can::pattern::DestructType::Optional(_, _) => {
// if we reach this stage, the optional field is present
// DestructType::Optional(loc_expr.value.clone())
DestructType::Required
}
roc_can::pattern::DestructType::Guard(_, loc_pattern) => {
DestructType::Guard(from_can_pattern(env, layout_cache, &loc_pattern.value))