Merge pull request #459 from aiken-lang/remove-multi-subjects

Cleanup implementation from multi-subjects when/is
This commit is contained in:
Matthias Benkort 2023-03-17 14:58:56 +01:00 committed by GitHub
commit 3eccc349aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 257 additions and 370 deletions

View File

@ -1,5 +1,3 @@
use std::{fmt, ops::Range, sync::Arc};
use crate::{
builtins::{self, bool},
expr::{TypedExpr, UntypedExpr},
@ -8,6 +6,8 @@ use crate::{
};
use miette::Diagnostic;
use owo_colors::{OwoColorize, Stream::Stdout};
use std::{fmt, ops::Range, sync::Arc};
use vec1::Vec1;
pub const ASSERT_VARIABLE: &str = "_try";
pub const CAPTURE_VARIABLE: &str = "_capture";
@ -909,26 +909,26 @@ pub type MultiPattern<PatternConstructor, Type> = Vec<Pattern<PatternConstructor
pub type UntypedMultiPattern = MultiPattern<(), ()>;
pub type TypedMultiPattern = MultiPattern<PatternConstructor, Arc<Type>>;
pub type TypedClause = Clause<TypedExpr, PatternConstructor, Arc<Type>>;
pub type UntypedClause = Clause<UntypedExpr, (), ()>;
#[derive(Debug, Clone, PartialEq)]
pub struct UntypedClause {
pub location: Span,
pub patterns: Vec1<Pattern<(), ()>>,
pub guard: Option<ClauseGuard<()>>,
pub then: UntypedExpr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Clause<Expr, PatternConstructor, Type> {
pub struct TypedClause {
pub location: Span,
pub pattern: MultiPattern<PatternConstructor, Type>,
pub alternative_patterns: Vec<MultiPattern<PatternConstructor, Type>>,
pub guard: Option<ClauseGuard<Type>>,
pub then: Expr,
pub pattern: Pattern<PatternConstructor, Arc<Type>>,
pub guard: Option<ClauseGuard<Arc<Type>>>,
pub then: TypedExpr,
}
impl TypedClause {
pub fn location(&self) -> Span {
Span {
start: self
.pattern
.get(0)
.map(|p| p.location().start)
.unwrap_or_default(),
start: self.pattern.location().start,
end: self.then.location().end,
}
}
@ -936,29 +936,6 @@ impl TypedClause {
pub fn find_node(&self, byte_index: usize) -> Option<&TypedExpr> {
self.then.find_node(byte_index)
}
pub fn desugarize(self) -> Vec<Self> {
let mut alternative_patterns = self
.alternative_patterns
.into_iter()
.map(|pattern| Self {
location: self.location,
pattern,
alternative_patterns: vec![],
guard: self.guard.clone(),
then: self.then.clone(),
})
.collect::<Vec<_>>();
let mut clauses = vec![Self {
alternative_patterns: vec![],
..self
}];
clauses.append(&mut alternative_patterns);
clauses
}
}
pub type UntypedClauseGuard = ClauseGuard<()>;

View File

@ -18,8 +18,8 @@ use uplc::{
use crate::{
air::Air,
ast::{
AssignmentKind, BinOp, Clause, ClauseGuard, Constant, DataType, Pattern, Span, TypedArg,
TypedDataType, UnOp,
AssignmentKind, BinOp, ClauseGuard, Constant, DataType, Pattern, Span, TypedArg,
TypedClause, TypedDataType, UnOp,
},
expr::TypedExpr,
tipo::{PatternConstructor, Type, TypeVar, ValueConstructor, ValueConstructorVariant},
@ -396,9 +396,7 @@ pub fn convert_data_to_type(term: Term<Name>, field_type: &Arc<Type>) -> Term<Na
}
}
pub fn rearrange_clauses(
clauses: Vec<Clause<TypedExpr, PatternConstructor, Arc<Type>>>,
) -> Vec<Clause<TypedExpr, PatternConstructor, Arc<Type>>> {
pub fn rearrange_clauses(clauses: Vec<TypedClause>) -> Vec<TypedClause> {
let mut sorted_clauses = clauses;
// if we have a list sort clauses so we can plug holes for cases not covered by clauses
@ -406,11 +404,11 @@ pub fn rearrange_clauses(
// let's sort clauses by a safer manner
// TODO: how shall tails be weighted? Since any clause after will not run
sorted_clauses.sort_by(|clause1, clause2| {
let clause1_len = match &clause1.pattern[0] {
let clause1_len = match &clause1.pattern {
Pattern::List { elements, tail, .. } => elements.len() + usize::from(tail.is_some()),
_ => 10000000,
};
let clause2_len = match &clause2.pattern[0] {
let clause2_len = match &clause2.pattern {
Pattern::List { elements, tail, .. } => elements.len() + usize::from(tail.is_some()),
_ => 10000001,
};
@ -427,7 +425,7 @@ pub fn rearrange_clauses(
// If we have a catch all, use that. Otherwise use todo which will result in error
// TODO: fill in todo label with description
let plug_in_then = match &sorted_clauses[sorted_clauses.len() - 1].pattern[0] {
let plug_in_then = match &sorted_clauses[sorted_clauses.len() - 1].pattern {
Pattern::Var { name, .. } => {
assign_plug_in_name = Some(name);
sorted_clauses[sorted_clauses.len() - 1].clone().then
@ -452,7 +450,7 @@ pub fn rearrange_clauses(
};
for (index, clause) in sorted_clauses.iter().enumerate() {
if let Pattern::List { elements, .. } = &clause.pattern[0] {
if let Pattern::List { elements, .. } = &clause.pattern {
// found a hole and now we plug it
while elems_len < elements.len() {
let mut discard_elems = vec![];
@ -466,9 +464,9 @@ pub fn rearrange_clauses(
// If we have a named catch all then in scope the name and create list of discards, otherwise list of discards
let clause_to_fill = if let Some(name) = assign_plug_in_name {
Clause {
TypedClause {
location: Span::empty(),
pattern: vec![Pattern::Assign {
pattern: Pattern::Assign {
name: name.clone(),
location: Span::empty(),
pattern: Pattern::List {
@ -477,20 +475,18 @@ pub fn rearrange_clauses(
tail: None,
}
.into(),
}],
alternative_patterns: vec![],
},
guard: None,
then: plug_in_then.clone(),
}
} else {
Clause {
TypedClause {
location: Span::empty(),
pattern: vec![Pattern::List {
pattern: Pattern::List {
location: Span::empty(),
elements: discard_elems,
tail: None,
}],
alternative_patterns: vec![],
},
guard: None,
then: plug_in_then.clone(),
}
@ -502,7 +498,7 @@ pub fn rearrange_clauses(
}
// if we have a pattern with no clause guards and a tail then no lists will get past here to other clauses
match &clause.pattern[0] {
match &clause.pattern {
Pattern::Var { .. } => {
last_clause_index = index + 1;
last_clause_set = true;
@ -533,14 +529,13 @@ pub fn rearrange_clauses(
// If the last condition doesn't have a catch all or tail then add a catch all with a todo
if index == sorted_clauses.len() - 1 {
if let Pattern::List { tail: None, .. } = &clause.pattern[0] {
final_clauses.push(Clause {
if let Pattern::List { tail: None, .. } = &clause.pattern {
final_clauses.push(TypedClause {
location: Span::empty(),
pattern: vec![Pattern::Discard {
pattern: Pattern::Discard {
name: "_".to_string(),
location: Span::empty(),
}],
alternative_patterns: vec![],
},
guard: None,
then: plug_in_then.clone(),
});

View File

@ -4,9 +4,9 @@ use vec1::Vec1;
use crate::{
ast::{
Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg, Clause,
DefinitionLocation, IfBranch, Pattern, RecordUpdateSpread, Span, TraceKind,
TypedRecordUpdateArg, UnOp, UntypedRecordUpdateArg,
Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
DefinitionLocation, IfBranch, Pattern, RecordUpdateSpread, Span, TraceKind, TypedClause,
TypedRecordUpdateArg, UnOp, UntypedClause, UntypedRecordUpdateArg,
},
builtins::void,
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
@ -102,8 +102,8 @@ pub enum TypedExpr {
When {
location: Span,
tipo: Arc<Type>,
subjects: Vec<Self>,
clauses: Vec<Clause<Self, PatternConstructor, Arc<Type>>>,
subject: Box<Self>,
clauses: Vec<TypedClause>,
},
If {
@ -355,10 +355,9 @@ impl TypedExpr {
TypedExpr::Assignment { value, .. } => value.find_node(byte_index),
TypedExpr::When {
subjects, clauses, ..
} => subjects
.iter()
.find_map(|subject| subject.find_node(byte_index))
subject, clauses, ..
} => subject
.find_node(byte_index)
.or_else(|| {
clauses
.iter()
@ -481,8 +480,8 @@ pub enum UntypedExpr {
When {
location: Span,
subjects: Vec<Self>,
clauses: Vec<Clause<Self, (), ()>>,
subject: Box<Self>,
clauses: Vec<UntypedClause>,
},
If {

View File

@ -728,8 +728,8 @@ impl<'comments> Formatter<'comments> {
} => self.trace(kind, text, then),
UntypedExpr::When {
subjects, clauses, ..
} => self.when(subjects, clauses),
subject, clauses, ..
} => self.when(subject, clauses),
UntypedExpr::FieldAccess {
label, container, ..
@ -933,14 +933,11 @@ impl<'comments> Formatter<'comments> {
pub fn when<'a>(
&mut self,
subjects: &'a [UntypedExpr],
subject: &'a UntypedExpr,
clauses: &'a [UntypedClause],
) -> Document<'a> {
let subjects_doc = break_("when", "when ")
.append(join(
subjects.iter().map(|s| self.wrap_expr(s)),
break_(",", ", "),
))
.append(self.wrap_expr(subject))
.nest(INDENT)
.append(break_("", " "))
.append("is {")
@ -1449,9 +1446,7 @@ impl<'comments> Formatter<'comments> {
let space_before = self.pop_empty_lines(clause.location.start);
let after_position = clause.location.end;
let clause_doc = join(
std::iter::once(&clause.pattern)
.chain(&clause.alternative_patterns)
.map(|p| join(p.iter().map(|p| self.pattern(p)), ", ".to_doc())),
clause.patterns.iter().map(|p| self.pattern(p)),
" | ".to_doc(),
);
let clause_doc = match &clause.guard {

View File

@ -1,6 +1,3 @@
use chumsky::prelude::*;
use vec1::Vec1;
pub mod error;
pub mod extra;
pub mod lexer;
@ -13,10 +10,11 @@ use crate::{
},
expr,
};
use chumsky::prelude::*;
use error::ParseError;
use extra::ModuleExtra;
use token::Token;
use vec1::{vec1, Vec1};
pub fn module(
src: &str,
@ -923,7 +921,7 @@ pub fn expr_parser(
let when_clause_parser = pattern_parser()
.then(
just(Token::Vbar)
.ignore_then(pattern_parser().map(|pattern| vec![pattern]))
.ignore_then(pattern_parser())
.repeated()
.or_not(),
)
@ -962,26 +960,29 @@ pub fn expr_parser(
}),
)))
.map_with_span(
|(((pattern, alternative_patterns_opt), guard), then), span| ast::UntypedClause {
location: span,
pattern: vec![pattern],
alternative_patterns: alternative_patterns_opt.unwrap_or_default(),
guard,
then,
|(((pattern, alternative_patterns_opt), guard), then), span| {
let mut patterns = vec1![pattern];
patterns.append(&mut alternative_patterns_opt.unwrap_or_default());
ast::UntypedClause {
location: span,
patterns,
guard,
then,
}
},
);
let when_parser = just(Token::When)
// TODO: If subject is empty we should return ParseErrorType::ExpectedExpr,
.ignore_then(r.clone().separated_by(just(Token::Comma)))
.ignore_then(r.clone().map(Box::new))
.then_ignore(just(Token::Is))
.then_ignore(just(Token::LeftBrace))
// TODO: If clauses are empty we should return ParseErrorType::NoCaseClause
.then(when_clause_parser.repeated())
.then_ignore(just(Token::RightBrace))
.map_with_span(|(subjects, clauses), span| expr::UntypedExpr::When {
.map_with_span(|(subject, clauses), span| expr::UntypedExpr::When {
location: span,
subjects,
subject,
clauses,
});

View File

@ -1,11 +1,11 @@
use chumsky::prelude::*;
use indoc::indoc;
use pretty_assertions::assert_eq;
use crate::{
ast::{self, Constant, DataType, Function, ModuleConstant, Span, TypeAlias, Use},
expr, parser,
};
use chumsky::prelude::*;
use indoc::indoc;
use pretty_assertions::assert_eq;
use vec1::vec1;
fn assert_definitions(code: &str, definitions: Vec<ast::UntypedDefinition>) {
let (module, _extra) = parser::module(code, ast::ModuleKind::Validator).unwrap();
@ -933,39 +933,38 @@ fn when() {
}],
body: expr::UntypedExpr::When {
location: Span::new((), 23..132),
subjects: vec![expr::UntypedExpr::Var {
subject: Box::new(expr::UntypedExpr::Var {
location: Span::new((), 28..29),
name: "a".to_string(),
}],
}),
clauses: vec![
ast::Clause {
ast::UntypedClause {
location: Span::new((), 39..45),
pattern: vec![ast::Pattern::Int {
patterns: vec1![ast::Pattern::Int {
location: Span::new((), 39..40),
value: "2".to_string(),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Int {
location: Span::new((), 44..45),
value: "3".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 50..106),
pattern: vec![ast::Pattern::Int {
location: Span::new((), 50..51),
value: "1".to_string(),
}],
alternative_patterns: vec![
vec![ast::Pattern::Int {
patterns: vec1![
ast::Pattern::Int {
location: Span::new((), 50..51),
value: "1".to_string()
},
ast::Pattern::Int {
location: Span::new((), 54..55),
value: "4".to_string(),
}],
vec![ast::Pattern::Int {
},
ast::Pattern::Int {
location: Span::new((), 58..59),
value: "5".to_string(),
}],
},
],
guard: None,
then: expr::UntypedExpr::Sequence {
@ -991,26 +990,24 @@ fn when() {
],
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 111..117),
pattern: vec![ast::Pattern::Int {
patterns: vec1![ast::Pattern::Int {
location: Span::new((), 111..112),
value: "3".to_string(),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Int {
location: Span::new((), 116..117),
value: "9".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 122..128),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 122..123),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Int {
location: Span::new((), 127..128),
@ -2154,13 +2151,13 @@ fn tuple_pattern() {
arguments: vec![],
body: expr::UntypedExpr::When {
location: Span::new((), 13..49),
subjects: vec![expr::UntypedExpr::Var {
subject: Box::new(expr::UntypedExpr::Var {
location: Span::new((), 18..19),
name: "a".to_string(),
}],
clauses: vec![ast::Clause {
}),
clauses: vec![ast::UntypedClause {
location: Span::new((), 29..45),
pattern: vec![ast::Pattern::Tuple {
patterns: vec1![ast::Pattern::Tuple {
location: Span::new((), 29..37),
elems: vec![
ast::Pattern::Var {
@ -2173,7 +2170,6 @@ fn tuple_pattern() {
},
],
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Var {
location: Span::new((), 41..45),
@ -2324,19 +2320,18 @@ fn clause_guards() {
arguments: vec![],
body: expr::UntypedExpr::When {
location: Span::new((), 13..250),
subjects: vec![expr::UntypedExpr::Var {
subject: Box::new(expr::UntypedExpr::Var {
location: Span::new((), 18..19),
name: "a".to_string(),
}],
}),
clauses: vec![
ast::Clause {
ast::UntypedClause {
location: Span::new((), 29..44),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 29..30),
}],
alternative_patterns: vec![],
guard: Some(ast::ClauseGuard::Constant(ast::Constant::Int {
guard: Some(ast::UntypedClauseGuard::Constant(ast::Constant::Int {
location: Span::new((), 34..36),
value: "42".to_string(),
})),
@ -2345,14 +2340,13 @@ fn clause_guards() {
name: "Void".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 49..65),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 49..50),
}],
alternative_patterns: vec![],
guard: Some(ast::ClauseGuard::Var {
guard: Some(ast::UntypedClauseGuard::Var {
location: Span::new((), 54..57),
name: "bar".to_string(),
tipo: (),
@ -2362,14 +2356,13 @@ fn clause_guards() {
name: "Void".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 70..87),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 70..71),
}],
alternative_patterns: vec![],
guard: Some(ast::ClauseGuard::Var {
guard: Some(ast::UntypedClauseGuard::Var {
location: Span::new((), 75..79),
tipo: (),
name: "True".to_string(),
@ -2379,28 +2372,27 @@ fn clause_guards() {
name: "Void".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 92..116),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 92..93),
}],
alternative_patterns: vec![],
guard: Some(ast::ClauseGuard::Or {
guard: Some(ast::UntypedClauseGuard::Or {
location: Span::new((), 97..108),
left: Box::new(ast::ClauseGuard::Var {
left: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 97..98),
name: "a".to_string(),
tipo: (),
}),
right: Box::new(ast::ClauseGuard::And {
right: Box::new(ast::UntypedClauseGuard::And {
location: Span::new((), 102..108),
left: Box::new(ast::ClauseGuard::Var {
left: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 102..103),
name: "b".to_string(),
tipo: (),
}),
right: Box::new(ast::ClauseGuard::Var {
right: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 107..108),
name: "c".to_string(),
tipo: (),
@ -2412,29 +2404,28 @@ fn clause_guards() {
name: "Void".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 121..147),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 121..122),
}],
alternative_patterns: vec![],
guard: Some(ast::ClauseGuard::And {
guard: Some(ast::UntypedClauseGuard::And {
location: Span::new((), 127..139),
left: Box::new(ast::ClauseGuard::Or {
left: Box::new(ast::UntypedClauseGuard::Or {
location: Span::new((), 127..133),
left: Box::new(ast::ClauseGuard::Var {
left: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 127..128),
name: "a".to_string(),
tipo: (),
}),
right: Box::new(ast::ClauseGuard::Var {
right: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 132..133),
name: "b".to_string(),
tipo: (),
}),
}),
right: Box::new(ast::ClauseGuard::Var {
right: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 138..139),
name: "c".to_string(),
tipo: (),
@ -2445,39 +2436,38 @@ fn clause_guards() {
name: "Void".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 152..191),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 152..153),
}],
alternative_patterns: vec![],
guard: Some(ast::ClauseGuard::Or {
guard: Some(ast::UntypedClauseGuard::Or {
location: Span::new((), 157..183),
left: Box::new(ast::ClauseGuard::Or {
left: Box::new(ast::UntypedClauseGuard::Or {
location: Span::new((), 157..174),
left: Box::new(ast::ClauseGuard::LtEqInt {
left: Box::new(ast::UntypedClauseGuard::LtEqInt {
location: Span::new((), 157..164),
left: Box::new(ast::ClauseGuard::Var {
left: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 157..158),
name: "a".to_string(),
tipo: (),
}),
right: Box::new(ast::ClauseGuard::Constant(
right: Box::new(ast::UntypedClauseGuard::Constant(
ast::Constant::Int {
location: Span::new((), 162..164),
value: "42".to_string(),
},
)),
}),
right: Box::new(ast::ClauseGuard::GtInt {
right: Box::new(ast::UntypedClauseGuard::GtInt {
location: Span::new((), 168..174),
left: Box::new(ast::ClauseGuard::Var {
left: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 168..169),
name: "b".to_string(),
tipo: (),
}),
right: Box::new(ast::ClauseGuard::Constant(
right: Box::new(ast::UntypedClauseGuard::Constant(
ast::Constant::Int {
location: Span::new((), 172..174),
value: "14".to_string(),
@ -2485,41 +2475,44 @@ fn clause_guards() {
)),
}),
}),
right: Box::new(ast::ClauseGuard::Constant(ast::Constant::ByteArray {
location: Span::new((), 178..183),
bytes: String::from("str").into_bytes(),
preferred_format: ast::ByteArrayFormatPreference::Utf8String,
})),
right: Box::new(ast::UntypedClauseGuard::Constant(
ast::Constant::ByteArray {
location: Span::new((), 178..183),
bytes: String::from("str").into_bytes(),
preferred_format: ast::ByteArrayFormatPreference::Utf8String,
},
)),
}),
then: expr::UntypedExpr::Var {
location: Span::new((), 187..191),
name: "Void".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 196..222),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 196..197),
}],
alternative_patterns: vec![],
guard: Some(ast::ClauseGuard::And {
guard: Some(ast::UntypedClauseGuard::And {
location: Span::new((), 201..214),
left: Box::new(ast::ClauseGuard::Equals {
left: Box::new(ast::UntypedClauseGuard::Equals {
location: Span::new((), 201..208),
left: Box::new(ast::ClauseGuard::Var {
left: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 201..202),
name: "a".to_string(),
tipo: (),
}),
right: Box::new(ast::ClauseGuard::Constant(ast::Constant::Int {
location: Span::new((), 206..208),
value: "14".to_string(),
})),
right: Box::new(ast::UntypedClauseGuard::Constant(
ast::Constant::Int {
location: Span::new((), 206..208),
value: "14".to_string(),
},
)),
}),
right: Box::new(ast::ClauseGuard::Not {
right: Box::new(ast::UntypedClauseGuard::Not {
location: Span::new((), 212..214),
value: Box::new(ast::ClauseGuard::Var {
value: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 213..214),
name: "b".to_string(),
tipo: (),
@ -2531,18 +2524,17 @@ fn clause_guards() {
name: "Void".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 227..246),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 227..228),
}],
alternative_patterns: vec![],
guard: Some(ast::ClauseGuard::Not {
guard: Some(ast::UntypedClauseGuard::Not {
location: Span::new((), 232..238),
value: Box::new(ast::ClauseGuard::Not {
value: Box::new(ast::UntypedClauseGuard::Not {
location: Span::new((), 233..238),
value: Box::new(ast::ClauseGuard::Var {
value: Box::new(ast::UntypedClauseGuard::Var {
location: Span::new((), 234..238),
tipo: (),
name: "True".to_string(),
@ -2826,14 +2818,14 @@ fn parse_keyword_error() {
arguments: vec![],
body: expr::UntypedExpr::When {
location: Span::new((), 54..110),
subjects: vec![expr::UntypedExpr::Var {
subject: Box::new(expr::UntypedExpr::Var {
location: Span::new((), 59..60),
name: "x".to_string(),
}],
}),
clauses: vec![
ast::Clause {
ast::UntypedClause {
location: Span::new((), 72..89),
pattern: vec![ast::Pattern::Constructor {
patterns: vec1![ast::Pattern::Constructor {
is_record: false,
location: Span::new((), 72..81),
name: "Something".to_string(),
@ -2843,20 +2835,18 @@ fn parse_keyword_error() {
with_spread: false,
tipo: (),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Var {
location: Span::new((), 85..89),
name: "Void".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 96..106),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 96..97),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Trace {
kind: ast::TraceKind::Error,
@ -2927,14 +2917,14 @@ fn parse_keyword_todo() {
arguments: vec![],
body: expr::UntypedExpr::When {
location: Span::new((), 53..121),
subjects: vec![expr::UntypedExpr::Var {
subject: Box::new(expr::UntypedExpr::Var {
location: Span::new((), 58..59),
name: "x".to_string(),
}],
}),
clauses: vec![
ast::Clause {
ast::UntypedClause {
location: Span::new((), 71..82),
pattern: vec![ast::Pattern::Constructor {
patterns: vec1![ast::Pattern::Constructor {
is_record: false,
location: Span::new((), 71..74),
name: "Foo".to_string(),
@ -2944,7 +2934,6 @@ fn parse_keyword_todo() {
with_spread: false,
tipo: (),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Trace {
kind: ast::TraceKind::Todo,
@ -2958,9 +2947,9 @@ fn parse_keyword_todo() {
}),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 89..100),
pattern: vec![ast::Pattern::Constructor {
patterns: vec1![ast::Pattern::Constructor {
is_record: false,
location: Span::new((), 89..92),
name: "Bar".to_string(),
@ -2970,20 +2959,18 @@ fn parse_keyword_todo() {
with_spread: false,
tipo: (),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Var {
location: Span::new((), 96..100),
name: "True".to_string(),
},
},
ast::Clause {
ast::UntypedClause {
location: Span::new((), 107..117),
pattern: vec![ast::Pattern::Discard {
patterns: vec1![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 107..108),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Var {
location: Span::new((), 112..117),

View File

@ -315,22 +315,6 @@ From there, you can define 'increment', a function that takes a single argument
given: usize,
},
// TODO: Since we do not actually support patterns on multiple items, we won't likely ever
// encounter that error. We could simplify a bit the type-checker and get rid of that error
// eventually.
#[error(
"I counted {} different clauses in a multi-pattern instead of {}.\n",
given.if_supports_color(Stdout, |s| s.purple()),
expected.if_supports_color(Stdout, |s| s.purple()),
)]
#[diagnostic(code("arity::clause"))]
IncorrectNumClausePatterns {
#[label]
location: Span,
expected: usize,
given: usize,
},
#[error(
"I saw a pattern on a constructor that has {} field(s) be matched with {} argument(s).\n",
expected.if_supports_color(Stdout, |s| s.purple()),

View File

@ -1,3 +1,4 @@
use crate::ast::TypedPattern;
use std::{cmp::Ordering, collections::HashMap, sync::Arc};
use vec1::Vec1;
@ -5,10 +6,10 @@ use vec1::Vec1;
use crate::{
ast::{
Annotation, Arg, ArgName, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
Clause, ClauseGuard, Constant, IfBranch, RecordUpdateSpread, Span, TraceKind, Tracing,
TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch, TypedMultiPattern,
TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard, UntypedIfBranch,
UntypedMultiPattern, UntypedPattern, UntypedRecordUpdateArg,
ClauseGuard, Constant, IfBranch, RecordUpdateSpread, Span, TraceKind, Tracing, TypedArg,
TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch, TypedRecordUpdateArg, UnOp,
UntypedArg, UntypedClause, UntypedClauseGuard, UntypedIfBranch, UntypedPattern,
UntypedRecordUpdateArg,
},
builtins::{bool, byte_array, function, int, list, string, tuple},
expr::{TypedExpr, UntypedExpr},
@ -22,7 +23,7 @@ use super::{
hydrator::Hydrator,
pattern::PatternTyper,
pipe::PipeTyper,
PatternConstructor, RecordAccessor, Type, ValueConstructor, ValueConstructorVariant,
RecordAccessor, Type, ValueConstructor, ValueConstructorVariant,
};
#[derive(Debug)]
@ -45,44 +46,24 @@ pub(crate) struct ExprTyper<'a, 'b> {
impl<'a, 'b> ExprTyper<'a, 'b> {
fn check_when_exhaustiveness(
&mut self,
subjects_count: usize,
subjects: &[Arc<Type>],
typed_clauses: &[Clause<TypedExpr, PatternConstructor, Arc<Type>>],
subject: &Type,
typed_clauses: &[TypedClause],
location: Span,
) -> Result<(), Vec<String>> {
// Because exhaustiveness checking in presence of multiple subjects is similar
// to full exhaustiveness checking of tuples or other nested record patterns,
// and we currently only do only limited exhaustiveness checking of custom types
// at the top level of patterns, only consider case expressions with one subject.
if subjects_count != 1 {
return Ok(());
}
let subject_type = subjects
.get(0)
.expect("Asserted there's one case subject but found none");
let value_typ = collapse_links(subject_type.clone());
let value_typ = collapse_links(Arc::new(subject.clone()));
// Currently guards in exhaustiveness checking are assumed that they can fail,
// so we go through all clauses and pluck out only the patterns
// for clauses that don't have guards.
let mut patterns = Vec::new();
for clause in typed_clauses {
if let Clause { guard: None, .. } = clause {
// clause.pattern is a list of patterns for all subjects
if let Some(pattern) = clause.pattern.get(0) {
patterns.push(pattern.clone());
}
// A clause can be built with alternative patterns as well, e.g. `Audio(_) | Text(_) ->`.
// We're interested in all patterns so we build a flattened list.
for alternative_pattern in &clause.alternative_patterns {
// clause.alternative_pattern is a list of patterns for all subjects
if let Some(pattern) = alternative_pattern.get(0) {
patterns.push(pattern.clone());
}
}
if let TypedClause {
guard: None,
pattern,
..
} = clause
{
patterns.push(pattern.clone())
}
}
@ -309,10 +290,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
UntypedExpr::When {
location,
subjects,
subject,
clauses,
..
} => self.infer_when(subjects, clauses, location),
} => self.infer_when(*subject, clauses, location),
UntypedExpr::List {
location,
@ -1093,35 +1074,34 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
fn infer_clause(
&mut self,
clause: UntypedClause,
subjects: &[Arc<Type>],
) -> Result<TypedClause, Error> {
let Clause {
pattern,
alternative_patterns,
subject: &Type,
) -> Result<Vec<TypedClause>, Error> {
let UntypedClause {
patterns,
guard,
then,
location,
} = clause;
let (guard, then, typed_pattern, typed_alternatives) = self.in_new_scope(|scope| {
// Check the types
let (typed_pattern, typed_alternatives) =
scope.infer_clause_pattern(pattern, alternative_patterns, subjects, &location)?;
let (guard, then, typed_patterns) = self.in_new_scope(|scope| {
let typed_patterns = scope.infer_clause_pattern(patterns, subject, &location)?;
let guard = scope.infer_optional_clause_guard(guard)?;
let then = scope.infer(then)?;
Ok::<_, Error>((guard, then, typed_pattern, typed_alternatives))
Ok::<_, Error>((guard, then, typed_patterns))
})?;
Ok(Clause {
location,
pattern: typed_pattern,
alternative_patterns: typed_alternatives,
guard,
then,
})
Ok(typed_patterns
.into_iter()
.map(|pattern| TypedClause {
location,
pattern,
guard: guard.clone(),
then: then.clone(),
})
.collect())
}
fn infer_clause_guard(&mut self, guard: UntypedClauseGuard) -> Result<TypedClauseGuard, Error> {
@ -1333,26 +1313,23 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
fn infer_clause_pattern(
&mut self,
pattern: UntypedMultiPattern,
alternatives: Vec<UntypedMultiPattern>,
subjects: &[Arc<Type>],
patterns: Vec1<UntypedPattern>,
subject: &Type,
location: &Span,
) -> Result<(TypedMultiPattern, Vec<TypedMultiPattern>), Error> {
) -> Result<Vec<TypedPattern>, Error> {
let mut pattern_typer = PatternTyper::new(self.environment, &self.hydrator);
let typed_pattern = pattern_typer.infer_multi_pattern(pattern, subjects, location)?;
// Each case clause has one or more patterns that may match the
// subject in order for the clause to be selected, so we must type
// check every pattern.
let mut typed_alternatives = Vec::with_capacity(alternatives.len());
for m in alternatives {
typed_alternatives
.push(pattern_typer.infer_alternative_multi_pattern(m, subjects, location)?);
let mut typed_patterns = Vec::with_capacity(patterns.len());
for (ix, pattern) in patterns.into_iter().enumerate() {
if ix == 0 {
typed_patterns.push(pattern_typer.infer_pattern(pattern, subject)?);
} else {
typed_patterns
.push(pattern_typer.infer_alternative_pattern(pattern, subject, location)?);
}
}
Ok((typed_pattern, typed_alternatives))
Ok(typed_patterns)
}
// TODO: extract the type annotation checking into a infer_module_const
@ -1865,7 +1842,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
fn infer_when(
&mut self,
subjects: Vec<UntypedExpr>,
subject: UntypedExpr,
clauses: Vec<UntypedClause>,
location: Span,
) -> Result<TypedExpr, Error> {
@ -1873,49 +1850,38 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
// that suggests that a `let` binding should be used instead.
if clauses.len() == 1 {
self.environment.warnings.push(Warning::SingleWhenClause {
location: clauses[0].pattern[0].location(),
location: clauses[0].patterns[0].location(),
sample: UntypedExpr::Assignment {
location: Span::empty(),
value: Box::new(subjects[0].clone()),
pattern: clauses[0].pattern[0].clone(),
value: Box::new(subject.clone()),
pattern: clauses[0].patterns[0].clone(),
kind: AssignmentKind::Let,
annotation: None,
},
});
}
let subjects_count = subjects.len();
let mut typed_subjects = Vec::with_capacity(subjects_count);
let mut subject_types = Vec::with_capacity(subjects_count);
let mut typed_clauses = Vec::new();
let typed_subject = self.infer(subject)?;
let subject_type = typed_subject.tipo();
let return_type = self.new_unbound_var();
for subject in subjects {
let subject = self.infer(subject)?;
subject_types.push(subject.tipo());
typed_subjects.push(subject);
}
let mut typed_clauses = Vec::new();
for clause in clauses {
let typed_clause = self.infer_clause(clause, &subject_types)?;
for typed_clause in self.infer_clause(clause, &subject_type)? {
self.unify(
return_type.clone(),
typed_clause.then.tipo(),
typed_clause.location(),
false,
)
.map_err(|e| e.case_clause_mismatch())?;
self.unify(
return_type.clone(),
typed_clause.then.tipo(),
typed_clause.location(),
false,
)
.map_err(|e| e.case_clause_mismatch())?;
typed_clauses.append(&mut typed_clause.desugarize());
typed_clauses.push(typed_clause)
}
}
if let Err(unmatched) =
self.check_when_exhaustiveness(subjects_count, &subject_types, &typed_clauses, location)
self.check_when_exhaustiveness(&subject_type, &typed_clauses, location)
{
return Err(Error::NotExhaustivePatternMatch {
location,
@ -1927,7 +1893,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Ok(TypedExpr::When {
location,
tipo: return_type,
subjects: typed_subjects,
subject: Box::new(typed_subject),
clauses: typed_clauses,
})
}

View File

@ -15,7 +15,7 @@ use super::{
PatternConstructor, Type, ValueConstructorVariant,
};
use crate::{
ast::{CallArg, Pattern, Span, TypedPattern, UntypedMultiPattern, UntypedPattern},
ast::{CallArg, Pattern, Span, TypedPattern, UntypedPattern},
builtins::{int, list, tuple},
};
@ -96,14 +96,14 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
}
}
pub fn infer_alternative_multi_pattern(
pub fn infer_alternative_pattern(
&mut self,
multi_pattern: UntypedMultiPattern,
subjects: &[Arc<Type>],
pattern: UntypedPattern,
subject: &Type,
location: &Span,
) -> Result<Vec<TypedPattern>, Error> {
) -> Result<TypedPattern, Error> {
self.mode = PatternMode::Alternative(vec![]);
let typed_multi = self.infer_multi_pattern(multi_pattern, subjects, location)?;
let typed_pattern = self.infer_pattern(pattern, subject)?;
match &self.mode {
PatternMode::Initial => panic!("Pattern mode switched from Alternative to Initial"),
PatternMode::Alternative(assigned)
@ -123,32 +123,16 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
.clone(),
})
}
PatternMode::Alternative(_) => Ok(typed_multi),
PatternMode::Alternative(_) => Ok(typed_pattern),
}
}
pub fn infer_multi_pattern(
pub fn infer_pattern(
&mut self,
multi_pattern: UntypedMultiPattern,
subjects: &[Arc<Type>],
location: &Span,
) -> Result<Vec<TypedPattern>, Error> {
// If there are N subjects the multi-pattern is expected to be N patterns
if subjects.len() != multi_pattern.len() {
return Err(Error::IncorrectNumClausePatterns {
location: *location,
expected: subjects.len(),
given: multi_pattern.len(),
});
}
// Unify each pattern in the multi-pattern with the corresponding subject
let mut typed_multi = Vec::with_capacity(multi_pattern.len());
for (pattern, subject_type) in multi_pattern.into_iter().zip(subjects) {
let pattern = self.unify(pattern, subject_type.clone(), None, false)?;
typed_multi.push(pattern);
}
Ok(typed_multi)
pattern: UntypedPattern,
subject: &Type,
) -> Result<TypedPattern, Error> {
self.unify(pattern, Arc::new(subject.clone()), None, false)
}
/// When we have an assignment or a case expression we unify the pattern with the

View File

@ -20,7 +20,7 @@ use uplc::{
use crate::{
air::Air,
ast::{
ArgName, AssignmentKind, BinOp, Clause, Pattern, Span, TypedArg, TypedDataType,
ArgName, AssignmentKind, BinOp, Pattern, Span, TypedArg, TypedClause, TypedDataType,
TypedFunction, UnOp,
},
builder::{
@ -476,13 +476,11 @@ impl<'a> CodeGenerator<'a> {
ir_stack.append(&mut pattern_vec);
}
TypedExpr::When {
subjects, clauses, ..
subject, clauses, ..
} => {
let subject_name = format!("__subject_name_{}", self.id_gen.next());
let constr_var = format!("__constr_name_{}", self.id_gen.next());
// assuming one subject at the moment
let subject = subjects[0].clone();
let subject_tipo = subject.tipo();
if clauses.len() <= 1 {
let mut value_vec: Vec<Air> = vec![];
@ -491,10 +489,10 @@ impl<'a> CodeGenerator<'a> {
self.build_ir(&clauses[0].then, &mut value_vec, scope.clone());
self.build_ir(&subject, &mut subject_vec, scope.clone());
self.build_ir(subject, &mut subject_vec, scope.clone());
self.assignment_ir(
&clauses[0].pattern[0],
&clauses[0].pattern,
&mut pattern_vec,
&mut subject_vec,
&subject_tipo,
@ -508,12 +506,14 @@ impl<'a> CodeGenerator<'a> {
ir_stack.append(&mut pattern_vec);
ir_stack.append(&mut value_vec);
} else {
println!("{clauses:?}");
// HERE TODO
let clauses = if subject_tipo.is_list() {
rearrange_clauses(clauses.clone())
} else {
clauses.clone()
};
println!("{clauses:?}");
if let Some((last_clause, clauses)) = clauses.split_last() {
let mut pattern_vec = vec![];
@ -532,7 +532,7 @@ impl<'a> CodeGenerator<'a> {
scope.clone(),
);
let last_pattern = &last_clause.pattern[0];
let last_pattern = &last_clause.pattern;
let mut final_scope = scope.clone();
@ -571,7 +571,7 @@ impl<'a> CodeGenerator<'a> {
let mut subject_scope = scope.clone();
subject_scope.push(self.id_gen.next());
self.build_ir(&subject, ir_stack, subject_scope.clone());
self.build_ir(subject, ir_stack, subject_scope.clone());
let mut scope = scope;
scope.push(self.id_gen.next());
@ -603,7 +603,7 @@ impl<'a> CodeGenerator<'a> {
let mut scope = scope;
scope.push(self.id_gen.next());
self.build_ir(&subject, ir_stack, scope);
self.build_ir(subject, ir_stack, scope);
}
ir_stack.append(&mut pattern_vec);
@ -817,7 +817,7 @@ impl<'a> CodeGenerator<'a> {
&mut self,
ir_stack: &mut Vec<Air>,
clause_properties: &mut ClauseProperties,
clauses: &[Clause<TypedExpr, PatternConstructor, Arc<Type>>],
clauses: &[TypedClause],
subject_type: &Arc<Type>,
scope: Vec<u64>,
) {
@ -876,7 +876,7 @@ impl<'a> CodeGenerator<'a> {
} => {
let subject_name = original_subject_name.clone();
self.when_ir(
&clause.pattern[0],
&clause.pattern,
&mut clause_subject_vec,
&mut clause_then_vec,
subject_type,
@ -922,16 +922,16 @@ impl<'a> CodeGenerator<'a> {
..
} => {
let (current_clause_index, has_tail) =
if let Pattern::List { elements, tail, .. } = &clause.pattern[0] {
if let Pattern::List { elements, tail, .. } = &clause.pattern {
(elements.len(), tail.is_some())
} else if let Pattern::Assign { pattern, .. } = &clause.pattern[0] {
} else if let Pattern::Assign { pattern, .. } = &clause.pattern {
if let Pattern::List { elements, tail, .. } = pattern.as_ref() {
(elements.len(), tail.is_some())
} else {
unreachable!("{:#?}", pattern)
}
} else {
unreachable!("{:#?}", &clause.pattern[0])
unreachable!("{:#?}", &clause.pattern)
};
let prev_index = *current_index;
@ -943,7 +943,7 @@ impl<'a> CodeGenerator<'a> {
};
self.when_ir(
&clause.pattern[0],
&clause.pattern,
&mut clause_subject_vec,
&mut clause_then_vec,
subject_type,
@ -955,11 +955,10 @@ impl<'a> CodeGenerator<'a> {
None
} else {
let next_list_size = if let Pattern::List { elements, .. } =
&clauses[index + 1].pattern[0]
&clauses[index + 1].pattern
{
elements.len()
} else if let Pattern::Assign { pattern, .. } =
&clauses[index + 1].pattern[0]
} else if let Pattern::Assign { pattern, .. } = &clauses[index + 1].pattern
{
if let Pattern::List { elements, .. } = pattern.as_ref() {
elements.len()
@ -1008,7 +1007,7 @@ impl<'a> CodeGenerator<'a> {
let subject_name = original_subject_name.clone();
self.when_ir(
&clause.pattern[0],
&clause.pattern,
&mut clause_subject_vec,
&mut clause_then_vec,
subject_type,