diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index d46cd40d..2dba649c 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -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 = Vec; pub type TypedMultiPattern = MultiPattern>; -pub type TypedClause = Clause>; -pub type UntypedClause = Clause; +#[derive(Debug, Clone, PartialEq)] +pub struct UntypedClause { + pub location: Span, + pub patterns: Vec1>, + pub guard: Option>, + pub then: UntypedExpr, +} #[derive(Debug, Clone, PartialEq)] -pub struct Clause { +pub struct TypedClause { pub location: Span, - pub pattern: MultiPattern, - pub alternative_patterns: Vec>, - pub guard: Option>, - pub then: Expr, + pub pattern: Pattern>, + pub guard: Option>>, + 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 { - 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::>(); - - let mut clauses = vec![Self { - alternative_patterns: vec![], - ..self - }]; - - clauses.append(&mut alternative_patterns); - - clauses - } } pub type UntypedClauseGuard = ClauseGuard<()>; diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index bcee8f50..458b4e09 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -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, field_type: &Arc) -> Term>>, -) -> Vec>> { +pub fn rearrange_clauses(clauses: Vec) -> Vec { 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(), }); diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index 33a9a0c5..c88a86f6 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -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, - subjects: Vec, - clauses: Vec>>, + subject: Box, + clauses: Vec, }, 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, - clauses: Vec>, + subject: Box, + clauses: Vec, }, If { diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index c8bdf75f..53f132dd 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -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 { diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 4c0158a0..90e75a65 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -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, }); diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index f8660170..cc83922f 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -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) { 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), diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 3486256a..786db0ac 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -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()), diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 18710ee7..110a0ff0 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -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], - typed_clauses: &[Clause>], + subject: &Type, + typed_clauses: &[TypedClause], location: Span, ) -> Result<(), Vec> { - // 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], - ) -> Result { - let Clause { - pattern, - alternative_patterns, + subject: &Type, + ) -> Result, 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 { @@ -1333,26 +1313,23 @@ impl<'a, 'b> ExprTyper<'a, 'b> { fn infer_clause_pattern( &mut self, - pattern: UntypedMultiPattern, - alternatives: Vec, - subjects: &[Arc], + patterns: Vec1, + subject: &Type, location: &Span, - ) -> Result<(TypedMultiPattern, Vec), Error> { + ) -> Result, 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, + subject: UntypedExpr, clauses: Vec, location: Span, ) -> Result { @@ -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, }) } diff --git a/crates/aiken-lang/src/tipo/pattern.rs b/crates/aiken-lang/src/tipo/pattern.rs index 57f5ee86..a6c3dada 100644 --- a/crates/aiken-lang/src/tipo/pattern.rs +++ b/crates/aiken-lang/src/tipo/pattern.rs @@ -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], + pattern: UntypedPattern, + subject: &Type, location: &Span, - ) -> Result, Error> { + ) -> Result { 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], - location: &Span, - ) -> Result, 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 { + self.unify(pattern, Arc::new(subject.clone()), None, false) } /// When we have an assignment or a case expression we unify the pattern with the diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index cc2ea6ae..1863d09c 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -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 = 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, clause_properties: &mut ClauseProperties, - clauses: &[Clause>], + clauses: &[TypedClause], subject_type: &Arc, scope: Vec, ) { @@ -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,