Add 'Pair' pattern and rework internals to use it.
Currently, pattern-matching on 'Pair' is handled by treating Pair as a record, which comes as slightly odd given that it isn't actually a record and isn't user-defined. Thus now, every use of a record must distinguish between Pairs and other kind of records -- which screams for another variant constructor instead. We cannot use `Tuple` either for this, because then we have no ways to tell 2-tuples apart from pairs, which is the whole point here. So the most sensical thing to do is to define a new pattern `Pair` which is akin to tuples, but simpler since we know the number of elements and it's always 2.
This commit is contained in:
parent
897b5d1d7e
commit
91a7e77ab4
|
@ -1243,6 +1243,12 @@ pub enum Pattern<Constructor, Type> {
|
|||
tipo: Type,
|
||||
},
|
||||
|
||||
Pair {
|
||||
location: Span,
|
||||
fst: Box<Self>,
|
||||
snd: Box<Self>,
|
||||
},
|
||||
|
||||
Tuple {
|
||||
location: Span,
|
||||
elems: Vec<Self>,
|
||||
|
@ -1258,6 +1264,7 @@ impl<A, B> Pattern<A, B> {
|
|||
| Pattern::List { location, .. }
|
||||
| Pattern::Discard { location, .. }
|
||||
| Pattern::Tuple { location, .. }
|
||||
| Pattern::Pair { location, .. }
|
||||
| Pattern::Constructor { location, .. } => *location,
|
||||
}
|
||||
}
|
||||
|
@ -1327,6 +1334,19 @@ impl TypedPattern {
|
|||
_ => None,
|
||||
},
|
||||
|
||||
Pattern::Pair { fst, snd, .. } => match &**value {
|
||||
Type::Pair {
|
||||
fst: fst_v,
|
||||
snd: snd_v,
|
||||
..
|
||||
} => [fst, snd]
|
||||
.into_iter()
|
||||
.zip([fst_v, snd_v].iter())
|
||||
.find_map(|(e, t)| e.find_node(byte_index, t))
|
||||
.or(Some(Located::Pattern(self, value.clone()))),
|
||||
_ => None,
|
||||
},
|
||||
|
||||
Pattern::Constructor {
|
||||
arguments, tipo, ..
|
||||
} => match &**tipo {
|
||||
|
@ -1340,6 +1360,7 @@ impl TypedPattern {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: This function definition is weird, see where this is used and how.
|
||||
pub fn tipo(&self, value: &TypedExpr) -> Option<Rc<Type>> {
|
||||
match self {
|
||||
Pattern::Int { .. } => Some(builtins::int()),
|
||||
|
@ -1347,7 +1368,7 @@ impl TypedPattern {
|
|||
Pattern::Var { .. } | Pattern::Assign { .. } | Pattern::Discard { .. } => {
|
||||
Some(value.tipo())
|
||||
}
|
||||
Pattern::List { .. } | Pattern::Tuple { .. } => None,
|
||||
Pattern::List { .. } | Pattern::Tuple { .. } | Pattern::Pair { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1779,6 +1779,14 @@ impl<'comments> Formatter<'comments> {
|
|||
wrap_args(elems.iter().map(|e| (self.pattern(e), false))).group()
|
||||
}
|
||||
|
||||
Pattern::Pair { fst, snd, .. } => "Pair"
|
||||
.to_doc()
|
||||
.append("(")
|
||||
.append(self.pattern(fst))
|
||||
.append(",")
|
||||
.append(self.pattern(snd))
|
||||
.append(")"),
|
||||
|
||||
Pattern::List { elements, tail, .. } => {
|
||||
let elements_document =
|
||||
join(elements.iter().map(|e| self.pattern(e)), break_(",", ", "));
|
||||
|
|
|
@ -975,6 +975,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
AirTree::let_assignment(name, value, then)
|
||||
}
|
||||
}
|
||||
|
||||
Pattern::Assign { name, pattern, .. } => {
|
||||
let inner_pattern = self.assignment(
|
||||
pattern,
|
||||
|
@ -985,6 +986,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
);
|
||||
AirTree::let_assignment(name, value, inner_pattern)
|
||||
}
|
||||
|
||||
Pattern::Discard { name, .. } => {
|
||||
if props.full_check {
|
||||
let name = &format!("__discard_expect_{}", name);
|
||||
|
@ -1015,6 +1017,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
AirTree::no_op(then)
|
||||
}
|
||||
}
|
||||
|
||||
Pattern::List { elements, tail, .. } => {
|
||||
assert!(tipo.is_list());
|
||||
assert!(props.kind.is_expect());
|
||||
|
@ -1141,47 +1144,33 @@ impl<'a> CodeGenerator<'a> {
|
|||
)
|
||||
}
|
||||
}
|
||||
// Pairs overlap by using the Constructor pattern type
|
||||
// The logic is slightly different for pairs
|
||||
Pattern::Constructor {
|
||||
arguments,
|
||||
constructor: PatternConstructor::Record { name, field_map },
|
||||
tipo: constr_tipo,
|
||||
..
|
||||
} if tipo.is_pair() => {
|
||||
// Constr and Pair execution branch
|
||||
let field_map = field_map.clone();
|
||||
|
||||
Pattern::Pair {
|
||||
fst,
|
||||
snd,
|
||||
location: _,
|
||||
} => {
|
||||
let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
|
||||
|
||||
for (index, arg) in constr_tipo.get_inner_types().iter().enumerate() {
|
||||
for (index, arg) in tipo.get_inner_types().iter().enumerate() {
|
||||
let field_type = arg.clone();
|
||||
|
||||
type_map.insert(index, field_type);
|
||||
}
|
||||
|
||||
assert!(type_map.len() >= arguments.len());
|
||||
assert!(type_map.len() == 2);
|
||||
|
||||
let mut fields = vec![];
|
||||
|
||||
let then = arguments
|
||||
let then = [fst, snd]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rfold(then, |then, (index, arg)| {
|
||||
let label = arg.label.clone().unwrap_or_default();
|
||||
|
||||
let field_index = if let Some(field_map) = &field_map {
|
||||
*field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index)
|
||||
} else {
|
||||
index
|
||||
};
|
||||
|
||||
let field_name = match &arg.value {
|
||||
.rfold(then, |then, (field_index, arg)| {
|
||||
let field_name = match arg.as_ref() {
|
||||
Pattern::Var { name, .. } => name.to_string(),
|
||||
Pattern::Assign { name, .. } => name.to_string(),
|
||||
Pattern::Discard { name, .. } => {
|
||||
if props.full_check {
|
||||
format!("__discard_{}_{}", name, index)
|
||||
format!("__discard_{}_{}", name, field_index)
|
||||
} else {
|
||||
"_".to_string()
|
||||
}
|
||||
|
@ -1189,15 +1178,15 @@ impl<'a> CodeGenerator<'a> {
|
|||
_ => format!(
|
||||
"field_{}_span_{}_{}",
|
||||
field_index,
|
||||
arg.value.location().start,
|
||||
arg.value.location().end
|
||||
arg.location().start,
|
||||
arg.location().end
|
||||
),
|
||||
};
|
||||
|
||||
let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
|
||||
unreachable!(
|
||||
"Missing type for field {} of constr {}",
|
||||
field_index, name
|
||||
field_index, field_name
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -1205,7 +1194,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
let then = if field_name != "_" {
|
||||
self.assignment(
|
||||
&arg.value,
|
||||
arg,
|
||||
val,
|
||||
then,
|
||||
arg_type,
|
||||
|
@ -1232,7 +1221,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
// local var
|
||||
let constructor_name = format!(
|
||||
"__constructor_{}_span_{}_{}",
|
||||
name,
|
||||
"Pair",
|
||||
pattern.location().start,
|
||||
pattern.location().end
|
||||
);
|
||||
|
@ -1421,6 +1410,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
AirTree::let_assignment(constructor_name, value, then)
|
||||
}
|
||||
|
||||
Pattern::Tuple {
|
||||
elems, location, ..
|
||||
} => {
|
||||
|
@ -2529,75 +2519,40 @@ impl<'a> CodeGenerator<'a> {
|
|||
(AirTree::void(), list_assign)
|
||||
}
|
||||
|
||||
Pattern::Constructor {
|
||||
name,
|
||||
arguments,
|
||||
constructor,
|
||||
tipo: function_tipo,
|
||||
..
|
||||
} if subject_tipo.is_pair() => {
|
||||
assert!(
|
||||
matches!(function_tipo.as_ref().clone(), Type::Fn { .. })
|
||||
|| matches!(function_tipo.as_ref().clone(), Type::App { .. })
|
||||
);
|
||||
Pattern::Pair { fst, snd, .. } => {
|
||||
let items_type = subject_tipo.get_inner_types();
|
||||
|
||||
let field_map = match constructor {
|
||||
PatternConstructor::Record { field_map, .. } => field_map.clone(),
|
||||
};
|
||||
|
||||
let mut type_map: IndexMap<usize, Rc<Type>> = IndexMap::new();
|
||||
|
||||
for (index, arg) in function_tipo.arg_types().unwrap().iter().enumerate() {
|
||||
let field_type = arg.clone();
|
||||
type_map.insert(index, field_type);
|
||||
}
|
||||
|
||||
let mut fields = vec![];
|
||||
let mut name_index_assigns = vec![];
|
||||
|
||||
let next_then =
|
||||
arguments
|
||||
[fst, snd]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rfold(then, |inner_then, (index, arg)| {
|
||||
let label = arg.label.clone().unwrap_or_default();
|
||||
|
||||
let field_index = if let Some(field_map) = &field_map {
|
||||
*field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index)
|
||||
} else {
|
||||
index
|
||||
};
|
||||
|
||||
let field_name = match &arg.value {
|
||||
.rfold(then, |inner_then, (index, element)| {
|
||||
let elem_name = match element.as_ref() {
|
||||
Pattern::Var { name, .. } => Some(name.to_string()),
|
||||
Pattern::Assign { name, .. } => Some(name.to_string()),
|
||||
Pattern::Discard { .. } => None,
|
||||
_ => Some(format!(
|
||||
"field_{}_span_{}_{}",
|
||||
field_index,
|
||||
arg.value.location().start,
|
||||
arg.value.location().end
|
||||
"pair_index_{}_span_{}_{}",
|
||||
index,
|
||||
element.location().start,
|
||||
element.location().end
|
||||
)),
|
||||
};
|
||||
|
||||
let arg_type = type_map.get(&field_index).unwrap_or_else(|| {
|
||||
unreachable!(
|
||||
"Missing type for field {} of constr {}",
|
||||
field_index, name
|
||||
)
|
||||
});
|
||||
|
||||
let mut field_props = ClauseProperties::init_inner(
|
||||
arg_type,
|
||||
field_name.clone().unwrap_or_else(|| "_".to_string()),
|
||||
field_name.clone().unwrap_or_else(|| "_".to_string()),
|
||||
let mut pair_props = ClauseProperties::init_inner(
|
||||
&items_type[index],
|
||||
elem_name.clone().unwrap_or_else(|| "_".to_string()),
|
||||
elem_name.clone().unwrap_or_else(|| "_".to_string()),
|
||||
props.final_clause,
|
||||
);
|
||||
|
||||
let statement = if field_name.is_some() {
|
||||
let elem = if elem_name.is_some() {
|
||||
self.nested_clause_condition(
|
||||
&arg.value,
|
||||
arg_type,
|
||||
&mut field_props,
|
||||
element,
|
||||
&items_type[index],
|
||||
&mut pair_props,
|
||||
inner_then,
|
||||
)
|
||||
} else {
|
||||
|
@ -2605,21 +2560,21 @@ impl<'a> CodeGenerator<'a> {
|
|||
};
|
||||
|
||||
props.complex_clause =
|
||||
props.complex_clause || field_props.complex_clause;
|
||||
props.complex_clause || pair_props.complex_clause;
|
||||
|
||||
fields.push((field_name, arg_type.clone()));
|
||||
name_index_assigns.push((elem_name, index));
|
||||
|
||||
statement
|
||||
elem
|
||||
});
|
||||
|
||||
fields.reverse();
|
||||
name_index_assigns.reverse();
|
||||
|
||||
let field_assign = if fields.iter().all(|s| s.0.is_none()) {
|
||||
let field_assign = if name_index_assigns.iter().all(|s| s.0.is_none()) {
|
||||
next_then
|
||||
} else {
|
||||
AirTree::pair_access(
|
||||
fields[0].0.clone(),
|
||||
fields[1].0.clone(),
|
||||
name_index_assigns[0].0.clone(),
|
||||
name_index_assigns[1].0.clone(),
|
||||
subject_tipo.clone(),
|
||||
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
|
||||
None,
|
||||
|
@ -3020,9 +2975,8 @@ impl<'a> CodeGenerator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Pattern::Constructor { .. } if subject_tipo.is_pair() => {
|
||||
Pattern::Pair { .. } => {
|
||||
let (_, assign) = self.clause_pattern(pattern, subject_tipo, props, then);
|
||||
|
||||
assign
|
||||
}
|
||||
|
||||
|
|
|
@ -679,16 +679,12 @@ pub fn pattern_has_conditions(
|
|||
Pattern::Tuple { elems, .. } => elems
|
||||
.iter()
|
||||
.any(|elem| pattern_has_conditions(elem, data_types)),
|
||||
Pattern::Pair { fst, snd, .. } => {
|
||||
pattern_has_conditions(fst, data_types) || pattern_has_conditions(snd, data_types)
|
||||
}
|
||||
Pattern::Constructor {
|
||||
arguments, tipo, ..
|
||||
} => {
|
||||
if tipo.is_pair()
|
||||
|| (tipo.is_function() && tipo.return_type().map(|t| t.is_pair()).unwrap_or(false))
|
||||
{
|
||||
arguments
|
||||
.iter()
|
||||
.any(|arg| pattern_has_conditions(&arg.value, data_types))
|
||||
} else {
|
||||
let data_type = lookup_data_type_by_tipo(data_types, tipo)
|
||||
.unwrap_or_else(|| panic!("Data type not found: {:#?}", tipo));
|
||||
|
||||
|
@ -697,7 +693,6 @@ pub fn pattern_has_conditions(
|
|||
.iter()
|
||||
.any(|arg| pattern_has_conditions(&arg.value, data_types))
|
||||
}
|
||||
}
|
||||
Pattern::Assign { pattern, .. } => pattern_has_conditions(pattern, data_types),
|
||||
Pattern::Var { .. } | Pattern::Discard { .. } => false,
|
||||
}
|
||||
|
|
|
@ -433,6 +433,10 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Self::App { args, ..} looks fishy, because App's args are referring
|
||||
// to _type parameters_ not to value types unlike Fn's args. So this function
|
||||
// definition is probably wrong. Luckily, we likely never hit the `Self::App`
|
||||
// case at all.
|
||||
pub fn arg_types(&self) -> Option<Vec<Rc<Self>>> {
|
||||
match self {
|
||||
Self::Fn { args, .. } => Some(args.clone()),
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use std::{collections::BTreeMap, iter, ops::Deref};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
builtins::{self, PAIR},
|
||||
builtins::{self},
|
||||
tipo::{self, environment::Environment, error::Error},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::{collections::BTreeMap, iter, ops::Deref};
|
||||
|
||||
const NIL_NAME: &str = "[]";
|
||||
const CONS_NAME: &str = "::";
|
||||
|
@ -564,8 +562,6 @@ pub(super) fn simplify(
|
|||
constructor: super::PatternConstructor::Record { name, .. },
|
||||
..
|
||||
} => {
|
||||
let (empty, pair) = (&"".to_string(), &PAIR.to_string());
|
||||
|
||||
let (module, type_name, arity) = match tipo.deref() {
|
||||
tipo::Type::App {
|
||||
name: type_name,
|
||||
|
@ -578,13 +574,11 @@ pub(super) fn simplify(
|
|||
module,
|
||||
..
|
||||
} => (module, type_name, args.len()),
|
||||
tipo::Type::Pair { .. } => (empty, pair, 2),
|
||||
_ => {
|
||||
unreachable!("ret should be a Type::App or Type::Pair")
|
||||
unreachable!("ret should be a Type::App")
|
||||
}
|
||||
},
|
||||
tipo::Type::Pair { .. } => (empty, pair, 2),
|
||||
_ => unreachable!("tipo should be a Type::App or Type::Pair"),
|
||||
_ => unreachable!("tipo should be a Type::App"),
|
||||
};
|
||||
|
||||
let alts = environment.get_constructors_for_type(module, type_name, *location)?;
|
||||
|
@ -603,6 +597,13 @@ pub(super) fn simplify(
|
|||
|
||||
Ok(Pattern::Constructor(name.to_string(), alts, args))
|
||||
}
|
||||
ast::Pattern::Pair { fst, snd, location } => simplify(
|
||||
environment,
|
||||
&ast::Pattern::Tuple {
|
||||
elems: vec![*fst.clone(), *snd.clone()],
|
||||
location: *location,
|
||||
},
|
||||
),
|
||||
ast::Pattern::Tuple { elems, .. } => {
|
||||
let mut args = vec![];
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
ast::{CallArg, Pattern, Span, TypedPattern, UntypedPattern},
|
||||
builtins::{int, list, tuple},
|
||||
builtins::{int, list, pair, tuple},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::{
|
||||
|
@ -236,6 +236,46 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
|
|||
}),
|
||||
},
|
||||
|
||||
Pattern::Pair { fst, snd, location } => match collapse_links(tipo.clone()).deref() {
|
||||
Type::Pair {
|
||||
fst: t_fst,
|
||||
snd: t_snd,
|
||||
..
|
||||
} => {
|
||||
let fst = Box::new(self.unify(*fst, t_fst.clone(), None, false)?);
|
||||
let snd = Box::new(self.unify(*snd, t_snd.clone(), None, false)?);
|
||||
Ok(Pattern::Pair { fst, snd, location })
|
||||
}
|
||||
|
||||
Type::Var { .. } => {
|
||||
let t_fst = self.environment.new_unbound_var();
|
||||
let t_snd = self.environment.new_unbound_var();
|
||||
|
||||
self.environment.unify(
|
||||
pair(t_fst.clone(), t_snd.clone()),
|
||||
tipo,
|
||||
location,
|
||||
false,
|
||||
)?;
|
||||
|
||||
let fst = Box::new(self.unify(*fst, t_fst, None, false)?);
|
||||
let snd = Box::new(self.unify(*snd, t_snd, None, false)?);
|
||||
|
||||
Ok(Pattern::Pair { fst, snd, location })
|
||||
}
|
||||
|
||||
_ => Err(Error::CouldNotUnify {
|
||||
given: pair(
|
||||
self.environment.new_unbound_var(),
|
||||
self.environment.new_unbound_var(),
|
||||
),
|
||||
expected: tipo,
|
||||
situation: None,
|
||||
location,
|
||||
rigid_type_names: HashMap::new(),
|
||||
}),
|
||||
},
|
||||
|
||||
Pattern::Tuple { elems, location } => match collapse_links(tipo.clone()).deref() {
|
||||
Type::Tuple {
|
||||
elems: type_elems, ..
|
||||
|
|
Loading…
Reference in New Issue