mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
implement optional fields in function pattern matches
This commit is contained in:
parent
ab78725944
commit
20ddbeb528
5 changed files with 264 additions and 52 deletions
|
@ -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))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue