mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Canonicalization of list patterns
This commit is contained in:
parent
55ea3bbea2
commit
b0a8b85de3
13 changed files with 189 additions and 4 deletions
|
@ -56,6 +56,11 @@ pub enum Pattern {
|
|||
ext_var: Variable,
|
||||
destructs: Vec<Loc<RecordDestruct>>,
|
||||
},
|
||||
List {
|
||||
list_var: Variable,
|
||||
elem_var: Variable,
|
||||
patterns: ListPatterns,
|
||||
},
|
||||
NumLiteral(Variable, Box<str>, IntValue, NumBound),
|
||||
IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound),
|
||||
FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound),
|
||||
|
@ -92,6 +97,10 @@ impl Pattern {
|
|||
AppliedTag { whole_var, .. } => Some(*whole_var),
|
||||
UnwrappedOpaque { whole_var, .. } => Some(*whole_var),
|
||||
RecordDestructure { whole_var, .. } => Some(*whole_var),
|
||||
List {
|
||||
list_var: whole_var,
|
||||
..
|
||||
} => Some(*whole_var),
|
||||
NumLiteral(var, ..) => Some(*var),
|
||||
IntLiteral(var, ..) => Some(*var),
|
||||
FloatLiteral(var, ..) => Some(*var),
|
||||
|
@ -119,6 +128,7 @@ impl Pattern {
|
|||
| MalformedPattern(..)
|
||||
| AbilityMemberSpecialization { .. } => true,
|
||||
RecordDestructure { destructs, .. } => destructs.is_empty(),
|
||||
List { patterns, .. } => patterns.surely_exhaustive(),
|
||||
AppliedTag { .. }
|
||||
| NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
|
@ -145,6 +155,7 @@ impl Pattern {
|
|||
UnwrappedOpaque { opaque, .. } => C::Opaque(*opaque),
|
||||
RecordDestructure { destructs, .. } if destructs.is_empty() => C::EmptyRecord,
|
||||
RecordDestructure { .. } => C::Record,
|
||||
List { .. } => C::List,
|
||||
NumLiteral(..) => C::Num,
|
||||
IntLiteral(..) => C::Int,
|
||||
FloatLiteral(..) => C::Float,
|
||||
|
@ -161,6 +172,25 @@ impl Pattern {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ListPatterns {
|
||||
pub patterns: Vec<Loc<Pattern>>,
|
||||
/// Where a rest pattern splits patterns before and after it, if it does at all.
|
||||
/// If present, patterns at index >= the rest index appear after the rest pattern.
|
||||
/// For example:
|
||||
/// [ .., A, B ] -> patterns = [A, B], rest = 0
|
||||
/// [ A, .., B ] -> patterns = [A, B], rest = 1
|
||||
/// [ A, B, .. ] -> patterns = [A, B], rest = 2
|
||||
pub opt_rest: Option<usize>,
|
||||
}
|
||||
|
||||
impl ListPatterns {
|
||||
/// Is this list pattern the trivially-exhaustive pattern `[..]`?
|
||||
fn surely_exhaustive(&self) -> bool {
|
||||
self.patterns.is_empty() && matches!(self.opt_rest, Some(0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RecordDestruct {
|
||||
pub var: Variable,
|
||||
|
@ -621,8 +651,75 @@ pub fn canonicalize_pattern<'a>(
|
|||
unreachable!("should have been handled in RecordDestructure");
|
||||
}
|
||||
|
||||
List(..) => todo!(),
|
||||
ListRest => todo!(),
|
||||
List(patterns) => {
|
||||
// We want to admit the following cases:
|
||||
//
|
||||
// []
|
||||
// [..]
|
||||
// [.., P_1,* P_n]
|
||||
// [P_1,* P_n, ..]
|
||||
// [P_1,* P_m, .., P_n,* P_q]
|
||||
// [P_1,* P_n]
|
||||
//
|
||||
// So, a list-rest pattern can appear anywhere in a list pattern, but can appear at
|
||||
// most once.
|
||||
let elem_var = var_store.fresh();
|
||||
let list_var = var_store.fresh();
|
||||
|
||||
let mut rest_index = None;
|
||||
let mut can_pats = Vec::with_capacity(patterns.len());
|
||||
let mut opt_erroneous = None;
|
||||
|
||||
for (i, loc_pattern) in patterns.iter().enumerate() {
|
||||
match &loc_pattern.value {
|
||||
ListRest => match rest_index {
|
||||
None => {
|
||||
rest_index = Some(i);
|
||||
}
|
||||
Some(_) => {
|
||||
env.problem(Problem::MultipleListRestPattern {
|
||||
region: loc_pattern.region,
|
||||
});
|
||||
|
||||
opt_erroneous = Some(Pattern::MalformedPattern(
|
||||
MalformedPatternProblem::DuplicateListRestPattern,
|
||||
loc_pattern.region,
|
||||
));
|
||||
}
|
||||
},
|
||||
pattern => {
|
||||
let pat = canonicalize_pattern(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
output,
|
||||
pattern_type,
|
||||
pattern,
|
||||
loc_pattern.region,
|
||||
permit_shadows,
|
||||
);
|
||||
can_pats.push(pat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we encountered an erroneous pattern (e.g. one with shadowing),
|
||||
// use the resulting RuntimeError. Otherwise, return a successful record destructure.
|
||||
opt_erroneous.unwrap_or(Pattern::List {
|
||||
list_var,
|
||||
elem_var,
|
||||
patterns: ListPatterns {
|
||||
patterns: can_pats,
|
||||
opt_rest: rest_index,
|
||||
},
|
||||
})
|
||||
}
|
||||
ListRest => {
|
||||
// Parsing should make sure these only appear in list patterns, where we will generate
|
||||
// better contextual errors.
|
||||
let problem = MalformedPatternProblem::Unknown;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
|
||||
Malformed(_str) => {
|
||||
let problem = MalformedPatternProblem::Unknown;
|
||||
|
@ -739,6 +836,9 @@ impl<'a> BindingsFromPattern<'a> {
|
|||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| OpaqueNotInScope(..) => (),
|
||||
List { patterns, .. } => {
|
||||
stack.extend(patterns.patterns.iter().rev().map(Pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
BindingsFromPatternWork::Destruct(loc_destruct) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue