diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index 9b3c0251..018c918c 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -385,12 +385,18 @@ pub enum Annotation { location: Span, name: String, }, + + Tuple { + location: Span, + elems: Vec, + }, } impl Annotation { pub fn location(&self) -> Span { match self { Annotation::Fn { location, .. } + | Annotation::Tuple { location, .. } | Annotation::Var { location, .. } | Annotation::Hole { location, .. } | Annotation::Constructor { location, .. } => *location, @@ -421,6 +427,19 @@ impl Annotation { } _ => false, }, + Annotation::Tuple { elems, location: _ } => match other { + Annotation::Tuple { + elems: o_elems, + location: _, + } => { + elems.len() == o_elems.len() + && elems + .iter() + .zip(o_elems) + .all(|a| a.0.is_logically_equal(a.1)) + } + _ => false, + }, Annotation::Fn { arguments, ret, @@ -584,10 +603,11 @@ pub enum Pattern { with_spread: bool, tipo: Type, }, - // Tuple { - // location: Span, - // elems: Vec, - // }, + + Tuple { + location: Span, + elems: Vec, + }, } impl Pattern { @@ -600,7 +620,7 @@ impl Pattern { | Pattern::List { location, .. } | Pattern::Discard { location, .. } | Pattern::String { location, .. } - // | Pattern::Tuple { location, .. } + | Pattern::Tuple { location, .. } // | Pattern::Concatenate { location, .. } | Pattern::Constructor { location, .. } => *location, } diff --git a/crates/lang/src/builtins.rs b/crates/lang/src/builtins.rs index 07b356a5..198b46fc 100644 --- a/crates/lang/src/builtins.rs +++ b/crates/lang/src/builtins.rs @@ -438,6 +438,10 @@ pub fn byte_array() -> Arc { }) } +pub fn tuple(elems: Vec>) -> Arc { + Arc::new(Type::Tuple { elems }) +} + pub fn bool() -> Arc { Arc::new(Type::App { args: vec![], diff --git a/crates/lang/src/expr.rs b/crates/lang/src/expr.rs index 80781de7..989f9384 100644 --- a/crates/lang/src/expr.rs +++ b/crates/lang/src/expr.rs @@ -91,12 +91,10 @@ pub enum TypedExpr { kind: AssignmentKind, }, - Try { + Trace { location: Span, tipo: Arc, - value: Box, then: Box, - pattern: Pattern>, }, When { @@ -130,11 +128,11 @@ pub enum TypedExpr { constructor: ModuleValueConstructor, }, - // Tuple { - // location: Span, - // tipo: Arc, - // elems: Vec, - // }, + Tuple { + location: Span, + tipo: Arc, + elems: Vec, + }, // TupleIndex { // location: Span, @@ -166,7 +164,7 @@ impl TypedExpr { match self { Self::Negate { .. } => bool(), Self::Var { constructor, .. } => constructor.tipo.clone(), - Self::Try { then, .. } => then.tipo(), + Self::Trace { then, .. } => then.tipo(), Self::Fn { tipo, .. } | Self::Int { tipo, .. } | Self::Todo { tipo, .. } @@ -175,7 +173,7 @@ impl TypedExpr { | Self::Call { tipo, .. } | Self::If { tipo, .. } | Self::BinOp { tipo, .. } - // | Self::Tuple { tipo, .. } + | Self::Tuple { tipo, .. } | Self::String { tipo, .. } | Self::ByteArray { tipo, .. } // | Self::TupleIndex { tipo, .. } @@ -194,7 +192,7 @@ impl TypedExpr { self, Self::Int { .. } | Self::List { .. } - // | Self::Tuple { .. } + | Self::Tuple { .. } | Self::String { .. } | Self::ByteArray { .. } ) @@ -209,13 +207,13 @@ impl TypedExpr { match self { TypedExpr::Fn { .. } | TypedExpr::Int { .. } - | TypedExpr::Try { .. } + | TypedExpr::Trace { .. } | TypedExpr::List { .. } | TypedExpr::Call { .. } | TypedExpr::When { .. } | TypedExpr::Todo { .. } | TypedExpr::BinOp { .. } - // | TypedExpr::Tuple { .. } + | TypedExpr::Tuple { .. } | TypedExpr::Negate { .. } | TypedExpr::String { .. } | TypedExpr::Sequence { .. } @@ -249,14 +247,14 @@ impl TypedExpr { match self { Self::Fn { location, .. } | Self::Int { location, .. } - | Self::Try { location, .. } + | Self::Trace { location, .. } | Self::Var { location, .. } | Self::Todo { location, .. } | Self::When { location, .. } | Self::Call { location, .. } | Self::List { location, .. } | Self::BinOp { location, .. } - // | Self::Tuple { location, .. } + | Self::Tuple { location, .. } | Self::String { location, .. } | Self::Negate { location, .. } | Self::Pipeline { location, .. } @@ -286,7 +284,7 @@ impl TypedExpr { pub fn location(&self) -> Span { match self { Self::Fn { location, .. } - | Self::Try { location, .. } + | Self::Trace { location, .. } | Self::Int { location, .. } | Self::Var { location, .. } | Self::Todo { location, .. } @@ -295,7 +293,7 @@ impl TypedExpr { | Self::If { location, .. } | Self::List { location, .. } | Self::BinOp { location, .. } - // | Self::Tuple { location, .. } + | Self::Tuple { location, .. } | Self::String { location, .. } | Self::Negate { location, .. } | Self::Sequence { location, .. } @@ -376,12 +374,10 @@ pub enum UntypedExpr { annotation: Option, }, - Try { + Trace { location: Span, - value: Box, - pattern: Pattern<(), ()>, then: Box, - annotation: Option, + text: Option, }, When { @@ -402,10 +398,10 @@ pub enum UntypedExpr { container: Box, }, - // Tuple { - // location: Span, - // elems: Vec, - // }, + Tuple { + location: Span, + elems: Vec, + }, // TupleIndex { // location: Span, // index: u64, @@ -481,7 +477,7 @@ impl UntypedExpr { pub fn location(&self) -> Span { match self { - Self::Try { then, .. } => then.location(), + Self::Trace { then, .. } => then.location(), Self::PipeLine { expressions, .. } => expressions.last().location(), Self::Fn { location, .. } | Self::Var { location, .. } @@ -492,7 +488,7 @@ impl UntypedExpr { | Self::List { location, .. } | Self::ByteArray { location, .. } | Self::BinOp { location, .. } - // | Self::Tuple { location, .. } + | Self::Tuple { location, .. } | Self::String { location, .. } | Self::Assignment { location, .. } // | Self::TupleIndex { location, .. } @@ -519,7 +515,7 @@ impl UntypedExpr { .map(|e| e.start_byte_index()) .unwrap_or(location.start), Self::PipeLine { expressions, .. } => expressions.first().start_byte_index(), - Self::Try { location, .. } | Self::Assignment { location, .. } => location.start, + Self::Trace { location, .. } | Self::Assignment { location, .. } => location.start, _ => self.location().start, } } diff --git a/crates/lang/src/format.rs b/crates/lang/src/format.rs index a34d2d5d..7a0c0598 100644 --- a/crates/lang/src/format.rs +++ b/crates/lang/src/format.rs @@ -276,7 +276,7 @@ impl<'comments> Formatter<'comments> { let head = pub_(*public).append("const ").append(name.as_str()); let head = match annotation { None => head, - Some(t) => head.append(": ").append(self.type_ast(t)), + Some(t) => head.append(": ").append(self.annotation(t)), }; head.append(" = ").append(self.const_expr(value)) } @@ -410,7 +410,7 @@ impl<'comments> Formatter<'comments> { } } - fn type_ast<'a>(&mut self, t: &'a Annotation) -> Document<'a> { + fn annotation<'a>(&mut self, t: &'a Annotation) -> Document<'a> { match t { Annotation::Hole { name, .. } => name.to_doc(), @@ -430,15 +430,16 @@ impl<'comments> Formatter<'comments> { .append(self.type_arguments(args)) .group() .append(" ->") - .append(break_("", " ").append(self.type_ast(retrn)).nest(INDENT)), + .append(break_("", " ").append(self.annotation(retrn)).nest(INDENT)), Annotation::Var { name, .. } => name.to_doc(), + Annotation::Tuple { elems, .. } => "#".to_doc().append(self.type_arguments(elems)), } .group() } fn type_arguments<'a>(&mut self, args: &'a [Annotation]) -> Document<'a> { - wrap_args(args.iter().map(|t| (self.type_ast(t), false))) + wrap_args(args.iter().map(|t| (self.annotation(t), false))) } pub fn type_alias<'a>( @@ -457,7 +458,7 @@ impl<'comments> Formatter<'comments> { }; head.append(" =") - .append(line().append(self.type_ast(typ)).group().nest(INDENT)) + .append(line().append(self.annotation(typ)).group().nest(INDENT)) } fn fn_arg<'a, A>(&mut self, arg: &'a Arg) -> Document<'a> { @@ -465,7 +466,11 @@ impl<'comments> Formatter<'comments> { let doc = match &arg.annotation { None => arg.arg_name.to_doc(), - Some(a) => arg.arg_name.to_doc().append(": ").append(self.type_ast(a)), + Some(a) => arg + .arg_name + .to_doc() + .append(": ") + .append(self.annotation(a)), } .group(); @@ -489,7 +494,7 @@ impl<'comments> Formatter<'comments> { // Add return annotation let head = match return_annotation { - Some(anno) => head.append(" -> ").append(self.type_ast(anno)), + Some(anno) => head.append(" -> ").append(self.annotation(anno)), None => head, } .group(); @@ -526,7 +531,7 @@ impl<'comments> Formatter<'comments> { let header = match return_annotation { None => header, - Some(t) => header.append(" -> ").append(self.type_ast(t)), + Some(t) => header.append(" -> ").append(self.annotation(t)), }; header @@ -576,7 +581,7 @@ impl<'comments> Formatter<'comments> { let annotation = annotation .as_ref() - .map(|a| ": ".to_doc().append(self.type_ast(a))); + .map(|a| ": ".to_doc().append(self.annotation(a))); let doc = if then.is_some() { keyword.to_doc().force_break() @@ -697,13 +702,28 @@ impl<'comments> Formatter<'comments> { .. } => self.assignment(pattern, value, None, Some(*kind), annotation), - UntypedExpr::Try { - value, - pattern, - annotation, + UntypedExpr::Trace { + text: None, then, .. + } => "trace" + .to_doc() + .append(if self.pop_empty_lines(then.start_byte_index()) { + lines(2) + } else { + line() + }) + .append(self.expr(then)), + + UntypedExpr::Trace { + text: Some(l), then, .. - } => self.assignment(pattern, value, Some(then), None, annotation), + } => docvec!["trace(\"", l, "\")"] + .append(if self.pop_empty_lines(then.start_byte_index()) { + lines(2) + } else { + line() + }) + .append(self.expr(then)), UntypedExpr::When { subjects, clauses, .. @@ -719,6 +739,11 @@ impl<'comments> Formatter<'comments> { arguments: args, .. } => self.record_update(constructor, spread, args), + + UntypedExpr::Tuple { elems, .. } => "#" + .to_doc() + .append(wrap_args(elems.iter().map(|e| (self.wrap_expr(e), false)))) + .group(), }; commented(document, comments) } @@ -742,7 +767,7 @@ impl<'comments> Formatter<'comments> { ) -> Document<'a> { fn is_breakable(expr: &UntypedPattern) -> bool { match expr { - Pattern::List { .. } => true, + Pattern::Tuple { .. } | Pattern::List { .. } => true, Pattern::Constructor { arguments: args, .. } => !args.is_empty(), @@ -982,8 +1007,8 @@ impl<'comments> Formatter<'comments> { let arg_comments = self.pop_comments(location.start); let arg = match label { - Some(l) => l.to_doc().append(": ").append(self.type_ast(annotation)), - None => self.type_ast(annotation), + Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)), + None => self.annotation(annotation), }; commented( @@ -1007,8 +1032,8 @@ impl<'comments> Formatter<'comments> { let arg_comments = self.pop_comments(location.start); let arg = match label { - Some(l) => l.to_doc().append(": ").append(self.type_ast(annotation)), - None => self.type_ast(annotation), + Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)), + None => self.annotation(annotation), }; ( @@ -1127,7 +1152,7 @@ impl<'comments> Formatter<'comments> { match expr { UntypedExpr::Sequence { .. } | UntypedExpr::Assignment { .. } - | UntypedExpr::Try { .. } => "{" + | UntypedExpr::Trace { .. } => "{" .to_doc() .append(line().append(self.expr(expr)).nest(INDENT)) .append(line()) @@ -1158,7 +1183,7 @@ impl<'comments> Formatter<'comments> { fn case_clause_value<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> { match expr { - UntypedExpr::Try { .. } + UntypedExpr::Trace { .. } | UntypedExpr::Sequence { .. } | UntypedExpr::Assignment { .. } => " {" .to_doc() @@ -1245,6 +1270,11 @@ impl<'comments> Formatter<'comments> { Pattern::Discard { name, .. } => name.to_doc(), + Pattern::Tuple { elems, .. } => "#" + .to_doc() + .append(wrap_args(elems.iter().map(|e| (self.pattern(e), false)))) + .group(), + Pattern::List { elements, tail, .. } => { let elements_document = join(elements.iter().map(|e| self.pattern(e)), break_(",", ", ")); diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 89c2ed47..c1beddb6 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -327,20 +327,17 @@ pub fn anon_fn_param_parser() -> impl Parser impl Parser { recursive(|r| { choice(( - just(Token::Try) - .ignore_then(pattern_parser()) - .then(just(Token::Colon).ignore_then(type_parser()).or_not()) - .then_ignore(just(Token::Equal)) - .then(expr_parser(r.clone())) + just(Token::Trace) + .ignore_then( + select! {Token::String {value} => value} + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .or_not(), + ) .then(r.clone()) - .map_with_span(|(((pattern, annotation), value), then_), span| { - expr::UntypedExpr::Try { - location: span, - value: Box::new(value), - pattern, - then: Box::new(then_), - annotation, - } + .map_with_span(|(text, then_), span| expr::UntypedExpr::Trace { + location: span, + then: Box::new(then_), + text, }), expr_parser(r.clone()) .then(r.repeated()) @@ -389,6 +386,18 @@ pub fn expr_parser( label, }); + let tuple = just(Token::Hash) + .ignore_then( + r.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ) + .map_with_span(|elems, span| expr::UntypedExpr::Tuple { + location: span, + elems, + }); + let list_parser = just(Token::LeftSquare) .ignore_then(r.clone().separated_by(just(Token::Comma))) .then(choice(( @@ -562,6 +571,7 @@ pub fn expr_parser( int_parser, var_parser, todo_parser, + tuple, list_parser, anon_fn_parser, block_parser, @@ -835,12 +845,24 @@ pub fn expr_parser( pub fn type_parser() -> impl Parser { recursive(|r| { choice(( + // Type hole select! {Token::DiscardName { name } => name}.map_with_span(|name, span| { ast::Annotation::Hole { location: span, name, } }), + just(Token::Hash) + .ignore_then( + r.clone() + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ) + .map_with_span(|elems, span| ast::Annotation::Tuple { + location: span, + elems, + }), + // Function just(Token::Fn) .ignore_then( r.clone() @@ -855,6 +877,7 @@ pub fn type_parser() -> impl Parser arguments, ret: Box::new(ret), }), + // Constructor function select! {Token::UpName { name } => name} .then( r.clone() @@ -869,6 +892,7 @@ pub fn type_parser() -> impl Parser name, arguments: arguments.unwrap_or_default(), }), + // Constructor Module or type Variable select! {Token::Name { name } => name} .then( just(Token::Dot) @@ -890,6 +914,7 @@ pub fn type_parser() -> impl Parser arguments: arguments.unwrap_or_default(), } } else { + // TODO: parse_error(ParseErrorType::NotConstType, SrcSpan { start, end }) ast::Annotation::Var { location: span, name: mod_name, @@ -1058,6 +1083,16 @@ pub fn pattern_parser() -> impl Parser impl Parser impl Parser Some(pat), Some(Some(pat)) => { - // use emit - // return parse_error(ParseErrorType::InvalidTailPattern, pat.location()) + emit(ParseError::expected_input_found( + pat.location(), + None, + Some(error::Pattern::Match), + )); + Some(pat) } // There is a tail but it has no content, implicit discard diff --git a/crates/lang/src/parser/error.rs b/crates/lang/src/parser/error.rs index cc161972..35b9ff55 100644 --- a/crates/lang/src/parser/error.rs +++ b/crates/lang/src/parser/error.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, fmt}; +use std::collections::HashSet; use miette::Diagnostic; @@ -72,7 +72,7 @@ pub enum ErrorKind { #[error("unexpected end")] UnexpectedEnd, #[error("unexpected {0}")] - #[diagnostic(help("try removing it"))] + #[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))] Unexpected(Pattern), #[error("unclosed {start}")] Unclosed { @@ -87,12 +87,22 @@ pub enum ErrorKind { #[derive(Debug, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)] pub enum Pattern { + #[error("{0:?}")] Char(char), + #[error("{0}")] + #[diagnostic(help("try removing it"))] Token(Token), + #[error("literal")] Literal, + #[error("type name")] TypeIdent, + #[error("indentifier")] TermIdent, + #[error("end of input")] End, + #[error("pattern")] + #[diagnostic(help("list spread in match can only have a discard or var"))] + Match, } impl From for Pattern { @@ -105,16 +115,3 @@ impl From for Pattern { Self::Token(tok) } } - -impl fmt::Display for Pattern { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Pattern::Token(token) => write!(f, "{}", token), - Pattern::Char(c) => write!(f, "{:?}", c), - Pattern::Literal => write!(f, "literal"), - Pattern::TypeIdent => write!(f, "type name"), - Pattern::TermIdent => write!(f, "identifier"), - Pattern::End => write!(f, "end of input"), - } - } -} diff --git a/crates/lang/src/parser/lexer.rs b/crates/lang/src/parser/lexer.rs index ce9539ea..af6aeb9f 100644 --- a/crates/lang/src/parser/lexer.rs +++ b/crates/lang/src/parser/lexer.rs @@ -31,6 +31,7 @@ pub fn lexer() -> impl Parser, Error = ParseError> { just('|').to(Token::Vbar), just("&&").to(Token::AmperAmper), just("\n\n").to(Token::EmptyLine), + just('#').to(Token::Hash), )); let grouping = choice(( @@ -74,7 +75,7 @@ pub fn lexer() -> impl Parser, Error = ParseError> { "pub" => Token::Pub, "use" => Token::Use, "todo" => Token::Todo, - "try" => Token::Try, + "trace" => Token::Trace, "type" => Token::Type, "when" => Token::When, _ => { diff --git a/crates/lang/src/parser/token.rs b/crates/lang/src/parser/token.rs index 208d6d86..8faaa339 100644 --- a/crates/lang/src/parser/token.rs +++ b/crates/lang/src/parser/token.rs @@ -69,7 +69,7 @@ pub enum Token { Pub, Use, Todo, - Try, + Trace, Type, When, } @@ -143,7 +143,7 @@ impl fmt::Display for Token { Token::Opaque => "opaque", Token::Pub => "pub", Token::Todo => "todo", - Token::Try => "try", + Token::Trace => "try", Token::Type => "type", }; write!(f, "\"{}\"", s) diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index 00bf8cba..1d04da41 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -45,12 +45,16 @@ pub enum Type { /// A type variable. See the contained `TypeVar` enum for more information. /// - Var { tipo: Arc> }, + Var { + tipo: Arc>, + }, // /// A tuple is an ordered collection of 0 or more values, each of which // /// can have a different type, so the `tuple` type is the sum of all the // /// contained types. // /// - // Tuple { elems: Vec> }, + Tuple { + elems: Vec>, + }, } impl Type { @@ -185,7 +189,7 @@ impl Type { Self::App { args, .. } => args.iter().find_map(|t| t.find_private_type()), - // Self::Tuple { elems, .. } => elems.iter().find_map(|t| t.find_private_type()), + Self::Tuple { elems, .. } => elems.iter().find_map(|t| t.find_private_type()), Self::Fn { ret, args, .. } => ret .find_private_type() .or_else(|| args.iter().find_map(|t| t.find_private_type())), diff --git a/crates/lang/src/tipo/environment.rs b/crates/lang/src/tipo/environment.rs index 27d9bfca..f9212e79 100644 --- a/crates/lang/src/tipo/environment.rs +++ b/crates/lang/src/tipo/environment.rs @@ -12,7 +12,7 @@ use crate::{ RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition, UnqualifiedImport, UntypedDefinition, Use, PIPE_VARIABLE, }, - builtins::{self, function, generic_var, unbound_var}, + builtins::{self, function, generic_var, tuple, unbound_var}, tipo::fields::FieldMap, IdGenerator, }; @@ -564,12 +564,13 @@ impl<'a> Environment<'a> { .collect(), self.instantiate(ret.clone(), ids, hydrator), ), - // Type::Tuple { elems } => tuple( - // elems - // .iter() - // .map(|t| self.instantiate(t.clone(), ids, hydrator)) - // .collect(), - // ), + + Type::Tuple { elems } => tuple( + elems + .iter() + .map(|t| self.instantiate(t.clone(), ids, hydrator)) + .collect(), + ), } } @@ -1400,6 +1401,7 @@ fn unify_unbound_type(tipo: Arc, own_id: u64, location: Span) -> Result<() for arg in args { unify_unbound_type(arg.clone(), own_id, location)? } + Ok(()) } @@ -1407,9 +1409,18 @@ fn unify_unbound_type(tipo: Arc, own_id: u64, location: Span) -> Result<() for arg in args { unify_unbound_type(arg.clone(), own_id, location)?; } + unify_unbound_type(ret.clone(), own_id, location) } + Type::Tuple { elems, .. } => { + for elem in elems { + unify_unbound_type(elem.clone(), own_id, location)? + } + + Ok(()) + } + Type::Var { .. } => unreachable!(), } } @@ -1591,11 +1602,12 @@ pub(crate) fn generalise(t: Arc, ctx_level: usize) -> Arc { .collect(), generalise(ret.clone(), ctx_level), ), - // Type::Tuple { elems } => tuple( - // elems - // .iter() - // .map(|t| generalise(t.clone(), ctx_level)) - // .collect(), - // ), + + Type::Tuple { elems } => tuple( + elems + .iter() + .map(|t| generalise(t.clone(), ctx_level)) + .collect(), + ), } } diff --git a/crates/lang/src/tipo/expr.rs b/crates/lang/src/tipo/expr.rs index 767da8ef..ed651d19 100644 --- a/crates/lang/src/tipo/expr.rs +++ b/crates/lang/src/tipo/expr.rs @@ -10,7 +10,7 @@ use crate::{ UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedIfBranch, UntypedMultiPattern, UntypedPattern, UntypedRecordUpdateArg, }, - builtins::{bool, byte_array, function, int, list, result, string}, + builtins::{bool, byte_array, function, int, list, string, tuple}, expr::{TypedExpr, UntypedExpr}, tipo::fields::FieldMap, }; @@ -245,9 +245,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> { location, } => self.infer_seq(location, expressions), - // UntypedExpr::Tuple { - // location, elems, .. - // } => self.infer_tuple(elems, location), + UntypedExpr::Tuple { + location, elems, .. + } => self.infer_tuple(elems, location), + UntypedExpr::String { location, value, .. } => Ok(self.infer_string(value, location)), @@ -278,14 +279,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { .. } => self.infer_assignment(pattern, *value, kind, &annotation, location), - UntypedExpr::Try { - location, - pattern, - value, - then, - annotation, - .. - } => self.infer_try(pattern, *value, *then, &annotation, location), + UntypedExpr::Trace { location, then, .. } => self.infer_trace(*then, location), UntypedExpr::When { location, @@ -1665,6 +1659,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } } + fn infer_tuple(&mut self, elems: Vec, location: Span) -> Result { + let mut typed_elems = vec![]; + + for elem in elems { + let typed_elem = self.infer(elem)?; + + typed_elems.push(typed_elem); + } + + let tipo = tuple(typed_elems.iter().map(|e| e.tipo()).collect()); + + Ok(TypedExpr::Tuple { + location, + elems: typed_elems, + tipo, + }) + } + fn infer_todo(&mut self, location: Span, kind: TodoKind, label: Option) -> TypedExpr { let tipo = self.new_unbound_var(); @@ -1681,66 +1693,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } } - fn infer_try( - &mut self, - pattern: UntypedPattern, - value: UntypedExpr, - then: UntypedExpr, - annotation: &Option, - location: Span, - ) -> Result { - let value = self.in_new_scope(|value_typer| value_typer.infer(value))?; - - let value_type = self.new_unbound_var(); - let try_error_type = self.new_unbound_var(); - - // Ensure that the value is a result - { - let v = value_type.clone(); - - let e = try_error_type.clone(); - - self.unify(result(v, e), value.tipo(), value.type_defining_location())?; - }; - - // Ensure the pattern matches the type of the value - let pattern = PatternTyper::new(self.environment, &self.hydrator).unify( - pattern, - value_type.clone(), - None, - )?; - + fn infer_trace(&mut self, then: UntypedExpr, location: Span) -> Result { // Check the type of the following code let then = self.infer(then)?; let tipo = then.tipo(); - // Ensure that a Result with the right error type is returned for `try` - { - let t = self.new_unbound_var(); - - self.unify( - result(t, try_error_type), - tipo.clone(), - then.type_defining_location(), - ) - .map_err(|e| e.inconsistent_try(tipo.is_result()))?; - } - - // Check that any type annotation is accurate. - if let Some(ann) = annotation { - let ann_typ = self - .type_from_annotation(ann) - .map(|t| self.instantiate(t, &mut HashMap::new()))?; - - self.unify(ann_typ, value_type, value.type_defining_location())?; - } - - Ok(TypedExpr::Try { + Ok(TypedExpr::Trace { location, tipo, - pattern, - value: Box::new(value), then: Box::new(then), }) } diff --git a/crates/lang/src/tipo/hydrator.rs b/crates/lang/src/tipo/hydrator.rs index 7ee7ccf6..bfe79950 100644 --- a/crates/lang/src/tipo/hydrator.rs +++ b/crates/lang/src/tipo/hydrator.rs @@ -1,6 +1,9 @@ use std::{collections::HashMap, sync::Arc}; -use crate::{ast::Annotation, builtins::function}; +use crate::{ + ast::Annotation, + builtins::{function, tuple}, +}; use super::{environment::Environment, error::Error, Type, TypeConstructor}; @@ -208,6 +211,18 @@ impl Hydrator { Annotation::Hole { location, .. } => Err(Error::UnexpectedTypeHole { location: *location, }), + + Annotation::Tuple { elems, .. } => { + let mut typed_elems = vec![]; + + for elem in elems { + let typed_elem = self.type_from_annotation(elem, environment)?; + + typed_elems.push(typed_elem) + } + + Ok(tuple(typed_elems)) + } } } } diff --git a/crates/lang/src/tipo/infer.rs b/crates/lang/src/tipo/infer.rs index 3a029b29..907b731d 100644 --- a/crates/lang/src/tipo/infer.rs +++ b/crates/lang/src/tipo/infer.rs @@ -459,7 +459,7 @@ fn str_to_keyword(word: &str) -> Option { "opaque" => Some(Token::Opaque), "pub" => Some(Token::Pub), "todo" => Some(Token::Todo), - "try" => Some(Token::Try), + "try" => Some(Token::Trace), "type" => Some(Token::Type), _ => None, } diff --git a/crates/lang/src/tipo/pattern.rs b/crates/lang/src/tipo/pattern.rs index a13abbab..0bcf00b9 100644 --- a/crates/lang/src/tipo/pattern.rs +++ b/crates/lang/src/tipo/pattern.rs @@ -9,14 +9,14 @@ use std::{ use itertools::Itertools; use super::{ - environment::{assert_no_labeled_arguments, EntityKind, Environment}, + environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment}, error::Error, hydrator::Hydrator, PatternConstructor, Type, ValueConstructor, ValueConstructorVariant, }; use crate::{ ast::{CallArg, Pattern, Span, TypedPattern, UntypedMultiPattern, UntypedPattern}, - builtins::{int, list, string}, + builtins::{int, list, string, tuple}, }; pub struct PatternTyper<'a, 'b> { @@ -354,55 +354,68 @@ impl<'a, 'b> PatternTyper<'a, 'b> { }), }, - // Pattern::Tuple { elems, location } => match collapse_links(tipo.clone()).deref() { - // Type::Tuple { elems: type_elems } => { - // if elems.len() != type_elems.len() { - // return Err(Error::IncorrectArity { - // labels: vec![], - // location, - // expected: type_elems.len(), - // given: elems.len(), - // }); - // } + Pattern::Tuple { elems, location } => match collapse_links(tipo.clone()).deref() { + Type::Tuple { elems: type_elems } => { + if elems.len() != type_elems.len() { + return Err(Error::IncorrectArity { + labels: vec![], + location, + expected: type_elems.len(), + given: elems.len(), + }); + } - // let elems = elems - // .into_iter() - // .zip(type_elems) - // .map(|(pattern, typ)| self.unify(pattern, typ.clone())) - // .try_collect()?; + let mut patterns = vec![]; - // Ok(Pattern::Tuple { elems, location }) - // } + for (pattern, typ) in elems.into_iter().zip(type_elems) { + let typed_pattern = self.unify(pattern, typ.clone(), None)?; - // Type::Var { .. } => { - // let elems_types: Vec<_> = (0..(elems.len())) - // .map(|_| self.environment.new_unbound_var()) - // .collect(); - // self.environment - // .unify(tuple(elems_types.clone()), type_) - // .map_err(|e| convert_unify_error(e, location))?; - // let elems = elems - // .into_iter() - // .zip(elems_types) - // .map(|(pattern, type_)| self.unify(pattern, type_)) - // .try_collect()?; - // Ok(Pattern::Tuple { elems, location }) - // } + patterns.push(typed_pattern); + } - // _ => { - // let elems_types = (0..(elems.len())) - // .map(|_| self.environment.new_unbound_var()) - // .collect(); + Ok(Pattern::Tuple { + elems: patterns, + location, + }) + } + + Type::Var { .. } => { + let elems_types: Vec<_> = (0..(elems.len())) + .map(|_| self.environment.new_unbound_var()) + .collect(); + + self.environment + .unify(tuple(elems_types.clone()), tipo, location)?; + + let mut patterns = vec![]; + + for (pattern, type_) in elems.into_iter().zip(elems_types) { + let typed_pattern = self.unify(pattern, type_, None)?; + + patterns.push(typed_pattern); + } + + Ok(Pattern::Tuple { + elems: patterns, + location, + }) + } + + _ => { + let elems_types = (0..(elems.len())) + .map(|_| self.environment.new_unbound_var()) + .collect(); + + Err(Error::CouldNotUnify { + given: tuple(elems_types), + expected: tipo, + situation: None, + location, + rigid_type_names: HashMap::new(), + }) + } + }, - // Err(Error::CouldNotUnify { - // given: tuple(elems_types), - // expected: type_, - // situation: None, - // location, - // rigid_type_names: hashmap![], - // }) - // } - // }, Pattern::Constructor { location, module, diff --git a/crates/lang/src/tipo/pretty.rs b/crates/lang/src/tipo/pretty.rs index d0c0232b..586838b8 100644 --- a/crates/lang/src/tipo/pretty.rs +++ b/crates/lang/src/tipo/pretty.rs @@ -74,6 +74,8 @@ impl Printer { .append(break_("", " ").append(self.print(ret)).nest(INDENT).group()), Type::Var { tipo: typ, .. } => self.type_var_doc(&typ.borrow()), + + Type::Tuple { elems, .. } => self.args_to_aiken_doc(elems).surround("#(", ")"), } } diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs index 92929073..fe6c8991 100644 --- a/crates/lang/src/uplc.rs +++ b/crates/lang/src/uplc.rs @@ -317,7 +317,7 @@ impl<'a> CodeGenerator<'a> { ValueConstructorVariant::Record { .. } => { match &*constructor.tipo { Type::App { .. } => {} - Type::Fn { .. } | Type::Var { .. } => {} + Type::Fn { .. } | Type::Var { .. } | Type::Tuple { .. } => {} }; } }; @@ -351,7 +351,7 @@ impl<'a> CodeGenerator<'a> { scope_level.scope_increment(1), &[], ), - TypedExpr::Try { .. } => todo!(), + TypedExpr::Trace { .. } => todo!(), TypedExpr::When { subjects, clauses, .. } => { @@ -531,6 +531,7 @@ impl<'a> CodeGenerator<'a> { TypedExpr::Todo { .. } => todo!(), TypedExpr::RecordUpdate { .. } => todo!(), TypedExpr::Negate { .. } => todo!(), + TypedExpr::Tuple { .. } => todo!(), } } @@ -763,6 +764,7 @@ impl<'a> CodeGenerator<'a> { _ => todo!(), }; } + Pattern::Tuple { .. } => todo!(), } } @@ -818,6 +820,7 @@ impl<'a> CodeGenerator<'a> { }, Type::Fn { .. } => todo!(), Type::Var { .. } => todo!(), + Type::Tuple { .. } => todo!(), }; if let Some(data_type) = self.data_types.get(&data_type_key) { @@ -871,6 +874,7 @@ impl<'a> CodeGenerator<'a> { Type::App { args, .. } => (*args[0]).clone(), Type::Fn { .. } => todo!(), Type::Var { .. } => todo!(), + Type::Tuple { .. } => todo!(), }; while !is_final_type { match current_tipo.clone() { @@ -891,6 +895,7 @@ impl<'a> CodeGenerator<'a> { } tipo::TypeVar::Generic { .. } => todo!(), }, + Type::Tuple { .. } => todo!(), }; } @@ -1282,6 +1287,7 @@ impl<'a> CodeGenerator<'a> { }, Type::Fn { .. } => todo!(), Type::Var { .. } => todo!(), + Type::Tuple { .. } => todo!(), }, BinOp::And => Term::Force( Term::Apply { @@ -1411,6 +1417,7 @@ impl<'a> CodeGenerator<'a> { } Type::Fn { .. } => todo!(), Type::Var { .. } => todo!(), + Type::Tuple { .. } => todo!(), }, BinOp::LtInt => Term::Apply { function: Term::Apply { @@ -1508,8 +1515,9 @@ impl<'a> CodeGenerator<'a> { Pattern::Discard { .. } => todo!(), Pattern::List { .. } => todo!(), Pattern::Constructor { .. } => todo!(), + Pattern::Tuple { .. } => todo!(), }, - TypedExpr::Try { .. } => todo!(), + TypedExpr::Trace { .. } => todo!(), TypedExpr::When { subjects, clauses, .. } => { @@ -1753,6 +1761,7 @@ impl<'a> CodeGenerator<'a> { Type::App { args, .. } => (*args[0]).clone(), Type::Fn { .. } => todo!(), Type::Var { .. } => todo!(), + Type::Tuple { .. } => todo!(), }; while !is_final_type { @@ -1774,6 +1783,7 @@ impl<'a> CodeGenerator<'a> { } tipo::TypeVar::Generic { .. } => todo!(), }, + Type::Tuple { .. } => todo!(), }; } @@ -2356,6 +2366,7 @@ impl<'a> CodeGenerator<'a> { TypedExpr::Todo { .. } => todo!(), TypedExpr::RecordUpdate { .. } => todo!(), TypedExpr::Negate { .. } => todo!(), + TypedExpr::Tuple { .. } => todo!(), } } @@ -2617,6 +2628,7 @@ impl<'a> CodeGenerator<'a> { Type::App { name, .. } => name, Type::Fn { .. } => todo!(), Type::Var { .. } => todo!(), + Type::Tuple { .. } => todo!(), }; (index, name.clone()) } @@ -2628,6 +2640,7 @@ impl<'a> CodeGenerator<'a> { Type::App { name, .. } => name, Type::Fn { .. } => todo!(), Type::Var { .. } => todo!(), + Type::Tuple { .. } => todo!(), }; (index, name.clone()) } diff --git a/examples/sample/validators/swap.ak b/examples/sample/validators/swap.ak index 20e4a512..10287fad 100644 --- a/examples/sample/validators/swap.ak +++ b/examples/sample/validators/swap.ak @@ -36,12 +36,17 @@ pub fn incrementor(counter: Int, target: Int) -> Int { } } +pub fn who(a: #(Int, Int)) -> #(Int, Int) { + #(1, 2) +} + pub fn spend( datum: sample.Datum, rdmr: Redeemer, ctx: spend.ScriptContext, ) -> Bool { - let x = Sell - let z = incrementor(0, 4) == 4 - z + when [1, 2, 3] is { + [a, b, ..] -> True + [] -> False + } }