feat: some new features

- tuples `#(Int, Int)`
- `trace` and `trace("text")`
This commit is contained in:
rvcas 2022-11-28 22:24:15 -05:00 committed by Lucas
parent 6066e3176c
commit 0823b78bf8
17 changed files with 335 additions and 223 deletions

View File

@ -385,12 +385,18 @@ pub enum Annotation {
location: Span, location: Span,
name: String, name: String,
}, },
Tuple {
location: Span,
elems: Vec<Self>,
},
} }
impl Annotation { impl Annotation {
pub fn location(&self) -> Span { pub fn location(&self) -> Span {
match self { match self {
Annotation::Fn { location, .. } Annotation::Fn { location, .. }
| Annotation::Tuple { location, .. }
| Annotation::Var { location, .. } | Annotation::Var { location, .. }
| Annotation::Hole { location, .. } | Annotation::Hole { location, .. }
| Annotation::Constructor { location, .. } => *location, | Annotation::Constructor { location, .. } => *location,
@ -421,6 +427,19 @@ impl Annotation {
} }
_ => false, _ => 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 { Annotation::Fn {
arguments, arguments,
ret, ret,
@ -584,10 +603,11 @@ pub enum Pattern<Constructor, Type> {
with_spread: bool, with_spread: bool,
tipo: Type, tipo: Type,
}, },
// Tuple {
// location: Span, Tuple {
// elems: Vec<Self>, location: Span,
// }, elems: Vec<Self>,
},
} }
impl<A, B> Pattern<A, B> { impl<A, B> Pattern<A, B> {
@ -600,7 +620,7 @@ impl<A, B> Pattern<A, B> {
| Pattern::List { location, .. } | Pattern::List { location, .. }
| Pattern::Discard { location, .. } | Pattern::Discard { location, .. }
| Pattern::String { location, .. } | Pattern::String { location, .. }
// | Pattern::Tuple { location, .. } | Pattern::Tuple { location, .. }
// | Pattern::Concatenate { location, .. } // | Pattern::Concatenate { location, .. }
| Pattern::Constructor { location, .. } => *location, | Pattern::Constructor { location, .. } => *location,
} }

View File

@ -438,6 +438,10 @@ pub fn byte_array() -> Arc<Type> {
}) })
} }
pub fn tuple(elems: Vec<Arc<Type>>) -> Arc<Type> {
Arc::new(Type::Tuple { elems })
}
pub fn bool() -> Arc<Type> { pub fn bool() -> Arc<Type> {
Arc::new(Type::App { Arc::new(Type::App {
args: vec![], args: vec![],

View File

@ -91,12 +91,10 @@ pub enum TypedExpr {
kind: AssignmentKind, kind: AssignmentKind,
}, },
Try { Trace {
location: Span, location: Span,
tipo: Arc<Type>, tipo: Arc<Type>,
value: Box<Self>,
then: Box<Self>, then: Box<Self>,
pattern: Pattern<PatternConstructor, Arc<Type>>,
}, },
When { When {
@ -130,11 +128,11 @@ pub enum TypedExpr {
constructor: ModuleValueConstructor, constructor: ModuleValueConstructor,
}, },
// Tuple { Tuple {
// location: Span, location: Span,
// tipo: Arc<Type>, tipo: Arc<Type>,
// elems: Vec<Self>, elems: Vec<Self>,
// }, },
// TupleIndex { // TupleIndex {
// location: Span, // location: Span,
@ -166,7 +164,7 @@ impl TypedExpr {
match self { match self {
Self::Negate { .. } => bool(), Self::Negate { .. } => bool(),
Self::Var { constructor, .. } => constructor.tipo.clone(), Self::Var { constructor, .. } => constructor.tipo.clone(),
Self::Try { then, .. } => then.tipo(), Self::Trace { then, .. } => then.tipo(),
Self::Fn { tipo, .. } Self::Fn { tipo, .. }
| Self::Int { tipo, .. } | Self::Int { tipo, .. }
| Self::Todo { tipo, .. } | Self::Todo { tipo, .. }
@ -175,7 +173,7 @@ impl TypedExpr {
| Self::Call { tipo, .. } | Self::Call { tipo, .. }
| Self::If { tipo, .. } | Self::If { tipo, .. }
| Self::BinOp { tipo, .. } | Self::BinOp { tipo, .. }
// | Self::Tuple { tipo, .. } | Self::Tuple { tipo, .. }
| Self::String { tipo, .. } | Self::String { tipo, .. }
| Self::ByteArray { tipo, .. } | Self::ByteArray { tipo, .. }
// | Self::TupleIndex { tipo, .. } // | Self::TupleIndex { tipo, .. }
@ -194,7 +192,7 @@ impl TypedExpr {
self, self,
Self::Int { .. } Self::Int { .. }
| Self::List { .. } | Self::List { .. }
// | Self::Tuple { .. } | Self::Tuple { .. }
| Self::String { .. } | Self::String { .. }
| Self::ByteArray { .. } | Self::ByteArray { .. }
) )
@ -209,13 +207,13 @@ impl TypedExpr {
match self { match self {
TypedExpr::Fn { .. } TypedExpr::Fn { .. }
| TypedExpr::Int { .. } | TypedExpr::Int { .. }
| TypedExpr::Try { .. } | TypedExpr::Trace { .. }
| TypedExpr::List { .. } | TypedExpr::List { .. }
| TypedExpr::Call { .. } | TypedExpr::Call { .. }
| TypedExpr::When { .. } | TypedExpr::When { .. }
| TypedExpr::Todo { .. } | TypedExpr::Todo { .. }
| TypedExpr::BinOp { .. } | TypedExpr::BinOp { .. }
// | TypedExpr::Tuple { .. } | TypedExpr::Tuple { .. }
| TypedExpr::Negate { .. } | TypedExpr::Negate { .. }
| TypedExpr::String { .. } | TypedExpr::String { .. }
| TypedExpr::Sequence { .. } | TypedExpr::Sequence { .. }
@ -249,14 +247,14 @@ impl TypedExpr {
match self { match self {
Self::Fn { location, .. } Self::Fn { location, .. }
| Self::Int { location, .. } | Self::Int { location, .. }
| Self::Try { location, .. } | Self::Trace { location, .. }
| Self::Var { location, .. } | Self::Var { location, .. }
| Self::Todo { location, .. } | Self::Todo { location, .. }
| Self::When { location, .. } | Self::When { location, .. }
| Self::Call { location, .. } | Self::Call { location, .. }
| Self::List { location, .. } | Self::List { location, .. }
| Self::BinOp { location, .. } | Self::BinOp { location, .. }
// | Self::Tuple { location, .. } | Self::Tuple { location, .. }
| Self::String { location, .. } | Self::String { location, .. }
| Self::Negate { location, .. } | Self::Negate { location, .. }
| Self::Pipeline { location, .. } | Self::Pipeline { location, .. }
@ -286,7 +284,7 @@ impl TypedExpr {
pub fn location(&self) -> Span { pub fn location(&self) -> Span {
match self { match self {
Self::Fn { location, .. } Self::Fn { location, .. }
| Self::Try { location, .. } | Self::Trace { location, .. }
| Self::Int { location, .. } | Self::Int { location, .. }
| Self::Var { location, .. } | Self::Var { location, .. }
| Self::Todo { location, .. } | Self::Todo { location, .. }
@ -295,7 +293,7 @@ impl TypedExpr {
| Self::If { location, .. } | Self::If { location, .. }
| Self::List { location, .. } | Self::List { location, .. }
| Self::BinOp { location, .. } | Self::BinOp { location, .. }
// | Self::Tuple { location, .. } | Self::Tuple { location, .. }
| Self::String { location, .. } | Self::String { location, .. }
| Self::Negate { location, .. } | Self::Negate { location, .. }
| Self::Sequence { location, .. } | Self::Sequence { location, .. }
@ -376,12 +374,10 @@ pub enum UntypedExpr {
annotation: Option<Annotation>, annotation: Option<Annotation>,
}, },
Try { Trace {
location: Span, location: Span,
value: Box<Self>,
pattern: Pattern<(), ()>,
then: Box<Self>, then: Box<Self>,
annotation: Option<Annotation>, text: Option<String>,
}, },
When { When {
@ -402,10 +398,10 @@ pub enum UntypedExpr {
container: Box<Self>, container: Box<Self>,
}, },
// Tuple { Tuple {
// location: Span, location: Span,
// elems: Vec<Self>, elems: Vec<Self>,
// }, },
// TupleIndex { // TupleIndex {
// location: Span, // location: Span,
// index: u64, // index: u64,
@ -481,7 +477,7 @@ impl UntypedExpr {
pub fn location(&self) -> Span { pub fn location(&self) -> Span {
match self { match self {
Self::Try { then, .. } => then.location(), Self::Trace { then, .. } => then.location(),
Self::PipeLine { expressions, .. } => expressions.last().location(), Self::PipeLine { expressions, .. } => expressions.last().location(),
Self::Fn { location, .. } Self::Fn { location, .. }
| Self::Var { location, .. } | Self::Var { location, .. }
@ -492,7 +488,7 @@ impl UntypedExpr {
| Self::List { location, .. } | Self::List { location, .. }
| Self::ByteArray { location, .. } | Self::ByteArray { location, .. }
| Self::BinOp { location, .. } | Self::BinOp { location, .. }
// | Self::Tuple { location, .. } | Self::Tuple { location, .. }
| Self::String { location, .. } | Self::String { location, .. }
| Self::Assignment { location, .. } | Self::Assignment { location, .. }
// | Self::TupleIndex { location, .. } // | Self::TupleIndex { location, .. }
@ -519,7 +515,7 @@ impl UntypedExpr {
.map(|e| e.start_byte_index()) .map(|e| e.start_byte_index())
.unwrap_or(location.start), .unwrap_or(location.start),
Self::PipeLine { expressions, .. } => expressions.first().start_byte_index(), 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, _ => self.location().start,
} }
} }

View File

@ -276,7 +276,7 @@ impl<'comments> Formatter<'comments> {
let head = pub_(*public).append("const ").append(name.as_str()); let head = pub_(*public).append("const ").append(name.as_str());
let head = match annotation { let head = match annotation {
None => head, 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)) 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 { match t {
Annotation::Hole { name, .. } => name.to_doc(), Annotation::Hole { name, .. } => name.to_doc(),
@ -430,15 +430,16 @@ impl<'comments> Formatter<'comments> {
.append(self.type_arguments(args)) .append(self.type_arguments(args))
.group() .group()
.append(" ->") .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::Var { name, .. } => name.to_doc(),
Annotation::Tuple { elems, .. } => "#".to_doc().append(self.type_arguments(elems)),
} }
.group() .group()
} }
fn type_arguments<'a>(&mut self, args: &'a [Annotation]) -> Document<'a> { 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>( pub fn type_alias<'a>(
@ -457,7 +458,7 @@ impl<'comments> Formatter<'comments> {
}; };
head.append(" =") 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<A>) -> Document<'a> { fn fn_arg<'a, A>(&mut self, arg: &'a Arg<A>) -> Document<'a> {
@ -465,7 +466,11 @@ impl<'comments> Formatter<'comments> {
let doc = match &arg.annotation { let doc = match &arg.annotation {
None => arg.arg_name.to_doc(), 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(); .group();
@ -489,7 +494,7 @@ impl<'comments> Formatter<'comments> {
// Add return annotation // Add return annotation
let head = match 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, None => head,
} }
.group(); .group();
@ -526,7 +531,7 @@ impl<'comments> Formatter<'comments> {
let header = match return_annotation { let header = match return_annotation {
None => header, None => header,
Some(t) => header.append(" -> ").append(self.type_ast(t)), Some(t) => header.append(" -> ").append(self.annotation(t)),
}; };
header header
@ -576,7 +581,7 @@ impl<'comments> Formatter<'comments> {
let annotation = annotation let annotation = annotation
.as_ref() .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() { let doc = if then.is_some() {
keyword.to_doc().force_break() keyword.to_doc().force_break()
@ -697,13 +702,28 @@ impl<'comments> Formatter<'comments> {
.. ..
} => self.assignment(pattern, value, None, Some(*kind), annotation), } => self.assignment(pattern, value, None, Some(*kind), annotation),
UntypedExpr::Try { UntypedExpr::Trace {
value, text: None, then, ..
pattern, } => "trace"
annotation, .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, 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 { UntypedExpr::When {
subjects, clauses, .. subjects, clauses, ..
@ -719,6 +739,11 @@ impl<'comments> Formatter<'comments> {
arguments: args, arguments: args,
.. ..
} => self.record_update(constructor, spread, 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) commented(document, comments)
} }
@ -742,7 +767,7 @@ impl<'comments> Formatter<'comments> {
) -> Document<'a> { ) -> Document<'a> {
fn is_breakable(expr: &UntypedPattern) -> bool { fn is_breakable(expr: &UntypedPattern) -> bool {
match expr { match expr {
Pattern::List { .. } => true, Pattern::Tuple { .. } | Pattern::List { .. } => true,
Pattern::Constructor { Pattern::Constructor {
arguments: args, .. arguments: args, ..
} => !args.is_empty(), } => !args.is_empty(),
@ -982,8 +1007,8 @@ impl<'comments> Formatter<'comments> {
let arg_comments = self.pop_comments(location.start); let arg_comments = self.pop_comments(location.start);
let arg = match label { let arg = match label {
Some(l) => l.to_doc().append(": ").append(self.type_ast(annotation)), Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)),
None => self.type_ast(annotation), None => self.annotation(annotation),
}; };
commented( commented(
@ -1007,8 +1032,8 @@ impl<'comments> Formatter<'comments> {
let arg_comments = self.pop_comments(location.start); let arg_comments = self.pop_comments(location.start);
let arg = match label { let arg = match label {
Some(l) => l.to_doc().append(": ").append(self.type_ast(annotation)), Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)),
None => self.type_ast(annotation), None => self.annotation(annotation),
}; };
( (
@ -1127,7 +1152,7 @@ impl<'comments> Formatter<'comments> {
match expr { match expr {
UntypedExpr::Sequence { .. } UntypedExpr::Sequence { .. }
| UntypedExpr::Assignment { .. } | UntypedExpr::Assignment { .. }
| UntypedExpr::Try { .. } => "{" | UntypedExpr::Trace { .. } => "{"
.to_doc() .to_doc()
.append(line().append(self.expr(expr)).nest(INDENT)) .append(line().append(self.expr(expr)).nest(INDENT))
.append(line()) .append(line())
@ -1158,7 +1183,7 @@ impl<'comments> Formatter<'comments> {
fn case_clause_value<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> { fn case_clause_value<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
match expr { match expr {
UntypedExpr::Try { .. } UntypedExpr::Trace { .. }
| UntypedExpr::Sequence { .. } | UntypedExpr::Sequence { .. }
| UntypedExpr::Assignment { .. } => " {" | UntypedExpr::Assignment { .. } => " {"
.to_doc() .to_doc()
@ -1245,6 +1270,11 @@ impl<'comments> Formatter<'comments> {
Pattern::Discard { name, .. } => name.to_doc(), 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, .. } => { Pattern::List { elements, tail, .. } => {
let elements_document = let elements_document =
join(elements.iter().map(|e| self.pattern(e)), break_(",", ", ")); join(elements.iter().map(|e| self.pattern(e)), break_(",", ", "));

View File

@ -327,20 +327,17 @@ pub fn anon_fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = Par
pub fn expr_seq_parser() -> impl Parser<Token, expr::UntypedExpr, Error = ParseError> { pub fn expr_seq_parser() -> impl Parser<Token, expr::UntypedExpr, Error = ParseError> {
recursive(|r| { recursive(|r| {
choice(( choice((
just(Token::Try) just(Token::Trace)
.ignore_then(pattern_parser()) .ignore_then(
.then(just(Token::Colon).ignore_then(type_parser()).or_not()) select! {Token::String {value} => value}
.then_ignore(just(Token::Equal)) .delimited_by(just(Token::LeftParen), just(Token::RightParen))
.then(expr_parser(r.clone())) .or_not(),
)
.then(r.clone()) .then(r.clone())
.map_with_span(|(((pattern, annotation), value), then_), span| { .map_with_span(|(text, then_), span| expr::UntypedExpr::Trace {
expr::UntypedExpr::Try { location: span,
location: span, then: Box::new(then_),
value: Box::new(value), text,
pattern,
then: Box::new(then_),
annotation,
}
}), }),
expr_parser(r.clone()) expr_parser(r.clone())
.then(r.repeated()) .then(r.repeated())
@ -389,6 +386,18 @@ pub fn expr_parser(
label, 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) let list_parser = just(Token::LeftSquare)
.ignore_then(r.clone().separated_by(just(Token::Comma))) .ignore_then(r.clone().separated_by(just(Token::Comma)))
.then(choice(( .then(choice((
@ -562,6 +571,7 @@ pub fn expr_parser(
int_parser, int_parser,
var_parser, var_parser,
todo_parser, todo_parser,
tuple,
list_parser, list_parser,
anon_fn_parser, anon_fn_parser,
block_parser, block_parser,
@ -835,12 +845,24 @@ pub fn expr_parser(
pub fn type_parser() -> impl Parser<Token, ast::Annotation, Error = ParseError> { pub fn type_parser() -> impl Parser<Token, ast::Annotation, Error = ParseError> {
recursive(|r| { recursive(|r| {
choice(( choice((
// Type hole
select! {Token::DiscardName { name } => name}.map_with_span(|name, span| { select! {Token::DiscardName { name } => name}.map_with_span(|name, span| {
ast::Annotation::Hole { ast::Annotation::Hole {
location: span, location: span,
name, 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) just(Token::Fn)
.ignore_then( .ignore_then(
r.clone() r.clone()
@ -855,6 +877,7 @@ pub fn type_parser() -> impl Parser<Token, ast::Annotation, Error = ParseError>
arguments, arguments,
ret: Box::new(ret), ret: Box::new(ret),
}), }),
// Constructor function
select! {Token::UpName { name } => name} select! {Token::UpName { name } => name}
.then( .then(
r.clone() r.clone()
@ -869,6 +892,7 @@ pub fn type_parser() -> impl Parser<Token, ast::Annotation, Error = ParseError>
name, name,
arguments: arguments.unwrap_or_default(), arguments: arguments.unwrap_or_default(),
}), }),
// Constructor Module or type Variable
select! {Token::Name { name } => name} select! {Token::Name { name } => name}
.then( .then(
just(Token::Dot) just(Token::Dot)
@ -890,6 +914,7 @@ pub fn type_parser() -> impl Parser<Token, ast::Annotation, Error = ParseError>
arguments: arguments.unwrap_or_default(), arguments: arguments.unwrap_or_default(),
} }
} else { } else {
// TODO: parse_error(ParseErrorType::NotConstType, SrcSpan { start, end })
ast::Annotation::Var { ast::Annotation::Var {
location: span, location: span,
name: mod_name, name: mod_name,
@ -1058,6 +1083,16 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
value, value,
} }
}), }),
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::UntypedPattern::Tuple {
location: span,
elems,
}),
just(Token::LeftSquare) just(Token::LeftSquare)
.ignore_then(r.clone().separated_by(just(Token::Comma))) .ignore_then(r.clone().separated_by(just(Token::Comma)))
.then(choice(( .then(choice((
@ -1066,7 +1101,7 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
just(Token::Comma).ignored().or_not().map(|_| None), just(Token::Comma).ignored().or_not().map(|_| None),
))) )))
.then_ignore(just(Token::RightSquare)) .then_ignore(just(Token::RightSquare))
.validate(|(elements, tail), span: Span, _emit| { .validate(|(elements, tail), span: Span, emit| {
let tail = match tail { let tail = match tail {
// There is a tail and it has a Pattern::Var or Pattern::Discard // There is a tail and it has a Pattern::Var or Pattern::Discard
Some(Some( Some(Some(
@ -1074,8 +1109,12 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
| ast::UntypedPattern::Discard { .. }), | ast::UntypedPattern::Discard { .. }),
)) => Some(pat), )) => Some(pat),
Some(Some(pat)) => { Some(Some(pat)) => {
// use emit emit(ParseError::expected_input_found(
// return parse_error(ParseErrorType::InvalidTailPattern, pat.location()) pat.location(),
None,
Some(error::Pattern::Match),
));
Some(pat) Some(pat)
} }
// There is a tail but it has no content, implicit discard // There is a tail but it has no content, implicit discard

View File

@ -1,4 +1,4 @@
use std::{collections::HashSet, fmt}; use std::collections::HashSet;
use miette::Diagnostic; use miette::Diagnostic;
@ -72,7 +72,7 @@ pub enum ErrorKind {
#[error("unexpected end")] #[error("unexpected end")]
UnexpectedEnd, UnexpectedEnd,
#[error("unexpected {0}")] #[error("unexpected {0}")]
#[diagnostic(help("try removing it"))] #[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))]
Unexpected(Pattern), Unexpected(Pattern),
#[error("unclosed {start}")] #[error("unclosed {start}")]
Unclosed { Unclosed {
@ -87,12 +87,22 @@ pub enum ErrorKind {
#[derive(Debug, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)] #[derive(Debug, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)]
pub enum Pattern { pub enum Pattern {
#[error("{0:?}")]
Char(char), Char(char),
#[error("{0}")]
#[diagnostic(help("try removing it"))]
Token(Token), Token(Token),
#[error("literal")]
Literal, Literal,
#[error("type name")]
TypeIdent, TypeIdent,
#[error("indentifier")]
TermIdent, TermIdent,
#[error("end of input")]
End, End,
#[error("pattern")]
#[diagnostic(help("list spread in match can only have a discard or var"))]
Match,
} }
impl From<char> for Pattern { impl From<char> for Pattern {
@ -105,16 +115,3 @@ impl From<Token> for Pattern {
Self::Token(tok) 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"),
}
}
}

View File

@ -31,6 +31,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
just('|').to(Token::Vbar), just('|').to(Token::Vbar),
just("&&").to(Token::AmperAmper), just("&&").to(Token::AmperAmper),
just("\n\n").to(Token::EmptyLine), just("\n\n").to(Token::EmptyLine),
just('#').to(Token::Hash),
)); ));
let grouping = choice(( let grouping = choice((
@ -74,7 +75,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
"pub" => Token::Pub, "pub" => Token::Pub,
"use" => Token::Use, "use" => Token::Use,
"todo" => Token::Todo, "todo" => Token::Todo,
"try" => Token::Try, "trace" => Token::Trace,
"type" => Token::Type, "type" => Token::Type,
"when" => Token::When, "when" => Token::When,
_ => { _ => {

View File

@ -69,7 +69,7 @@ pub enum Token {
Pub, Pub,
Use, Use,
Todo, Todo,
Try, Trace,
Type, Type,
When, When,
} }
@ -143,7 +143,7 @@ impl fmt::Display for Token {
Token::Opaque => "opaque", Token::Opaque => "opaque",
Token::Pub => "pub", Token::Pub => "pub",
Token::Todo => "todo", Token::Todo => "todo",
Token::Try => "try", Token::Trace => "try",
Token::Type => "type", Token::Type => "type",
}; };
write!(f, "\"{}\"", s) write!(f, "\"{}\"", s)

View File

@ -45,12 +45,16 @@ pub enum Type {
/// A type variable. See the contained `TypeVar` enum for more information. /// A type variable. See the contained `TypeVar` enum for more information.
/// ///
Var { tipo: Arc<RefCell<TypeVar>> }, Var {
tipo: Arc<RefCell<TypeVar>>,
},
// /// A tuple is an ordered collection of 0 or more values, each of which // /// 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 // /// can have a different type, so the `tuple` type is the sum of all the
// /// contained types. // /// contained types.
// /// // ///
// Tuple { elems: Vec<Arc<Type>> }, Tuple {
elems: Vec<Arc<Type>>,
},
} }
impl Type { impl Type {
@ -185,7 +189,7 @@ impl Type {
Self::App { args, .. } => args.iter().find_map(|t| t.find_private_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 Self::Fn { ret, args, .. } => ret
.find_private_type() .find_private_type()
.or_else(|| args.iter().find_map(|t| t.find_private_type())), .or_else(|| args.iter().find_map(|t| t.find_private_type())),

View File

@ -12,7 +12,7 @@ use crate::{
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition, RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition,
UnqualifiedImport, UntypedDefinition, Use, PIPE_VARIABLE, UnqualifiedImport, UntypedDefinition, Use, PIPE_VARIABLE,
}, },
builtins::{self, function, generic_var, unbound_var}, builtins::{self, function, generic_var, tuple, unbound_var},
tipo::fields::FieldMap, tipo::fields::FieldMap,
IdGenerator, IdGenerator,
}; };
@ -564,12 +564,13 @@ impl<'a> Environment<'a> {
.collect(), .collect(),
self.instantiate(ret.clone(), ids, hydrator), self.instantiate(ret.clone(), ids, hydrator),
), ),
// Type::Tuple { elems } => tuple(
// elems Type::Tuple { elems } => tuple(
// .iter() elems
// .map(|t| self.instantiate(t.clone(), ids, hydrator)) .iter()
// .collect(), .map(|t| self.instantiate(t.clone(), ids, hydrator))
// ), .collect(),
),
} }
} }
@ -1400,6 +1401,7 @@ fn unify_unbound_type(tipo: Arc<Type>, own_id: u64, location: Span) -> Result<()
for arg in args { for arg in args {
unify_unbound_type(arg.clone(), own_id, location)? unify_unbound_type(arg.clone(), own_id, location)?
} }
Ok(()) Ok(())
} }
@ -1407,9 +1409,18 @@ fn unify_unbound_type(tipo: Arc<Type>, own_id: u64, location: Span) -> Result<()
for arg in args { for arg in args {
unify_unbound_type(arg.clone(), own_id, location)?; unify_unbound_type(arg.clone(), own_id, location)?;
} }
unify_unbound_type(ret.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!(), Type::Var { .. } => unreachable!(),
} }
} }
@ -1591,11 +1602,12 @@ pub(crate) fn generalise(t: Arc<Type>, ctx_level: usize) -> Arc<Type> {
.collect(), .collect(),
generalise(ret.clone(), ctx_level), generalise(ret.clone(), ctx_level),
), ),
// Type::Tuple { elems } => tuple(
// elems Type::Tuple { elems } => tuple(
// .iter() elems
// .map(|t| generalise(t.clone(), ctx_level)) .iter()
// .collect(), .map(|t| generalise(t.clone(), ctx_level))
// ), .collect(),
),
} }
} }

View File

@ -10,7 +10,7 @@ use crate::{
UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedIfBranch, UntypedMultiPattern, UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedIfBranch, UntypedMultiPattern,
UntypedPattern, UntypedRecordUpdateArg, UntypedPattern, UntypedRecordUpdateArg,
}, },
builtins::{bool, byte_array, function, int, list, result, string}, builtins::{bool, byte_array, function, int, list, string, tuple},
expr::{TypedExpr, UntypedExpr}, expr::{TypedExpr, UntypedExpr},
tipo::fields::FieldMap, tipo::fields::FieldMap,
}; };
@ -245,9 +245,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
location, location,
} => self.infer_seq(location, expressions), } => self.infer_seq(location, expressions),
// UntypedExpr::Tuple { UntypedExpr::Tuple {
// location, elems, .. location, elems, ..
// } => self.infer_tuple(elems, location), } => self.infer_tuple(elems, location),
UntypedExpr::String { UntypedExpr::String {
location, value, .. location, value, ..
} => Ok(self.infer_string(value, location)), } => Ok(self.infer_string(value, location)),
@ -278,14 +279,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
.. ..
} => self.infer_assignment(pattern, *value, kind, &annotation, location), } => self.infer_assignment(pattern, *value, kind, &annotation, location),
UntypedExpr::Try { UntypedExpr::Trace { location, then, .. } => self.infer_trace(*then, location),
location,
pattern,
value,
then,
annotation,
..
} => self.infer_try(pattern, *value, *then, &annotation, location),
UntypedExpr::When { UntypedExpr::When {
location, location,
@ -1665,6 +1659,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
} }
} }
fn infer_tuple(&mut self, elems: Vec<UntypedExpr>, location: Span) -> Result<TypedExpr, Error> {
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<String>) -> TypedExpr { fn infer_todo(&mut self, location: Span, kind: TodoKind, label: Option<String>) -> TypedExpr {
let tipo = self.new_unbound_var(); let tipo = self.new_unbound_var();
@ -1681,66 +1693,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
} }
} }
fn infer_try( fn infer_trace(&mut self, then: UntypedExpr, location: Span) -> Result<TypedExpr, Error> {
&mut self,
pattern: UntypedPattern,
value: UntypedExpr,
then: UntypedExpr,
annotation: &Option<Annotation>,
location: Span,
) -> Result<TypedExpr, Error> {
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,
)?;
// Check the type of the following code // Check the type of the following code
let then = self.infer(then)?; let then = self.infer(then)?;
let tipo = then.tipo(); let tipo = then.tipo();
// Ensure that a Result with the right error type is returned for `try` Ok(TypedExpr::Trace {
{
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 {
location, location,
tipo, tipo,
pattern,
value: Box::new(value),
then: Box::new(then), then: Box::new(then),
}) })
} }

View File

@ -1,6 +1,9 @@
use std::{collections::HashMap, sync::Arc}; 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}; use super::{environment::Environment, error::Error, Type, TypeConstructor};
@ -208,6 +211,18 @@ impl Hydrator {
Annotation::Hole { location, .. } => Err(Error::UnexpectedTypeHole { Annotation::Hole { location, .. } => Err(Error::UnexpectedTypeHole {
location: *location, 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))
}
} }
} }
} }

View File

@ -459,7 +459,7 @@ fn str_to_keyword(word: &str) -> Option<Token> {
"opaque" => Some(Token::Opaque), "opaque" => Some(Token::Opaque),
"pub" => Some(Token::Pub), "pub" => Some(Token::Pub),
"todo" => Some(Token::Todo), "todo" => Some(Token::Todo),
"try" => Some(Token::Try), "try" => Some(Token::Trace),
"type" => Some(Token::Type), "type" => Some(Token::Type),
_ => None, _ => None,
} }

View File

@ -9,14 +9,14 @@ use std::{
use itertools::Itertools; use itertools::Itertools;
use super::{ use super::{
environment::{assert_no_labeled_arguments, EntityKind, Environment}, environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment},
error::Error, error::Error,
hydrator::Hydrator, hydrator::Hydrator,
PatternConstructor, Type, ValueConstructor, ValueConstructorVariant, PatternConstructor, Type, ValueConstructor, ValueConstructorVariant,
}; };
use crate::{ use crate::{
ast::{CallArg, Pattern, Span, TypedPattern, UntypedMultiPattern, UntypedPattern}, ast::{CallArg, Pattern, Span, TypedPattern, UntypedMultiPattern, UntypedPattern},
builtins::{int, list, string}, builtins::{int, list, string, tuple},
}; };
pub struct PatternTyper<'a, 'b> { 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() { Pattern::Tuple { elems, location } => match collapse_links(tipo.clone()).deref() {
// Type::Tuple { elems: type_elems } => { Type::Tuple { elems: type_elems } => {
// if elems.len() != type_elems.len() { if elems.len() != type_elems.len() {
// return Err(Error::IncorrectArity { return Err(Error::IncorrectArity {
// labels: vec![], labels: vec![],
// location, location,
// expected: type_elems.len(), expected: type_elems.len(),
// given: elems.len(), given: elems.len(),
// }); });
// } }
// let elems = elems let mut patterns = vec![];
// .into_iter()
// .zip(type_elems)
// .map(|(pattern, typ)| self.unify(pattern, typ.clone()))
// .try_collect()?;
// 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 { .. } => { patterns.push(typed_pattern);
// 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 })
// }
// _ => { Ok(Pattern::Tuple {
// let elems_types = (0..(elems.len())) elems: patterns,
// .map(|_| self.environment.new_unbound_var()) location,
// .collect(); })
}
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 { Pattern::Constructor {
location, location,
module, module,

View File

@ -74,6 +74,8 @@ impl Printer {
.append(break_("", " ").append(self.print(ret)).nest(INDENT).group()), .append(break_("", " ").append(self.print(ret)).nest(INDENT).group()),
Type::Var { tipo: typ, .. } => self.type_var_doc(&typ.borrow()), Type::Var { tipo: typ, .. } => self.type_var_doc(&typ.borrow()),
Type::Tuple { elems, .. } => self.args_to_aiken_doc(elems).surround("#(", ")"),
} }
} }

View File

@ -317,7 +317,7 @@ impl<'a> CodeGenerator<'a> {
ValueConstructorVariant::Record { .. } => { ValueConstructorVariant::Record { .. } => {
match &*constructor.tipo { match &*constructor.tipo {
Type::App { .. } => {} 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), scope_level.scope_increment(1),
&[], &[],
), ),
TypedExpr::Try { .. } => todo!(), TypedExpr::Trace { .. } => todo!(),
TypedExpr::When { TypedExpr::When {
subjects, clauses, .. subjects, clauses, ..
} => { } => {
@ -531,6 +531,7 @@ impl<'a> CodeGenerator<'a> {
TypedExpr::Todo { .. } => todo!(), TypedExpr::Todo { .. } => todo!(),
TypedExpr::RecordUpdate { .. } => todo!(), TypedExpr::RecordUpdate { .. } => todo!(),
TypedExpr::Negate { .. } => todo!(), TypedExpr::Negate { .. } => todo!(),
TypedExpr::Tuple { .. } => todo!(),
} }
} }
@ -763,6 +764,7 @@ impl<'a> CodeGenerator<'a> {
_ => todo!(), _ => todo!(),
}; };
} }
Pattern::Tuple { .. } => todo!(),
} }
} }
@ -818,6 +820,7 @@ impl<'a> CodeGenerator<'a> {
}, },
Type::Fn { .. } => todo!(), Type::Fn { .. } => todo!(),
Type::Var { .. } => todo!(), Type::Var { .. } => todo!(),
Type::Tuple { .. } => todo!(),
}; };
if let Some(data_type) = self.data_types.get(&data_type_key) { 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::App { args, .. } => (*args[0]).clone(),
Type::Fn { .. } => todo!(), Type::Fn { .. } => todo!(),
Type::Var { .. } => todo!(), Type::Var { .. } => todo!(),
Type::Tuple { .. } => todo!(),
}; };
while !is_final_type { while !is_final_type {
match current_tipo.clone() { match current_tipo.clone() {
@ -891,6 +895,7 @@ impl<'a> CodeGenerator<'a> {
} }
tipo::TypeVar::Generic { .. } => todo!(), tipo::TypeVar::Generic { .. } => todo!(),
}, },
Type::Tuple { .. } => todo!(),
}; };
} }
@ -1282,6 +1287,7 @@ impl<'a> CodeGenerator<'a> {
}, },
Type::Fn { .. } => todo!(), Type::Fn { .. } => todo!(),
Type::Var { .. } => todo!(), Type::Var { .. } => todo!(),
Type::Tuple { .. } => todo!(),
}, },
BinOp::And => Term::Force( BinOp::And => Term::Force(
Term::Apply { Term::Apply {
@ -1411,6 +1417,7 @@ impl<'a> CodeGenerator<'a> {
} }
Type::Fn { .. } => todo!(), Type::Fn { .. } => todo!(),
Type::Var { .. } => todo!(), Type::Var { .. } => todo!(),
Type::Tuple { .. } => todo!(),
}, },
BinOp::LtInt => Term::Apply { BinOp::LtInt => Term::Apply {
function: Term::Apply { function: Term::Apply {
@ -1508,8 +1515,9 @@ impl<'a> CodeGenerator<'a> {
Pattern::Discard { .. } => todo!(), Pattern::Discard { .. } => todo!(),
Pattern::List { .. } => todo!(), Pattern::List { .. } => todo!(),
Pattern::Constructor { .. } => todo!(), Pattern::Constructor { .. } => todo!(),
Pattern::Tuple { .. } => todo!(),
}, },
TypedExpr::Try { .. } => todo!(), TypedExpr::Trace { .. } => todo!(),
TypedExpr::When { TypedExpr::When {
subjects, clauses, .. subjects, clauses, ..
} => { } => {
@ -1753,6 +1761,7 @@ impl<'a> CodeGenerator<'a> {
Type::App { args, .. } => (*args[0]).clone(), Type::App { args, .. } => (*args[0]).clone(),
Type::Fn { .. } => todo!(), Type::Fn { .. } => todo!(),
Type::Var { .. } => todo!(), Type::Var { .. } => todo!(),
Type::Tuple { .. } => todo!(),
}; };
while !is_final_type { while !is_final_type {
@ -1774,6 +1783,7 @@ impl<'a> CodeGenerator<'a> {
} }
tipo::TypeVar::Generic { .. } => todo!(), tipo::TypeVar::Generic { .. } => todo!(),
}, },
Type::Tuple { .. } => todo!(),
}; };
} }
@ -2356,6 +2366,7 @@ impl<'a> CodeGenerator<'a> {
TypedExpr::Todo { .. } => todo!(), TypedExpr::Todo { .. } => todo!(),
TypedExpr::RecordUpdate { .. } => todo!(), TypedExpr::RecordUpdate { .. } => todo!(),
TypedExpr::Negate { .. } => todo!(), TypedExpr::Negate { .. } => todo!(),
TypedExpr::Tuple { .. } => todo!(),
} }
} }
@ -2617,6 +2628,7 @@ impl<'a> CodeGenerator<'a> {
Type::App { name, .. } => name, Type::App { name, .. } => name,
Type::Fn { .. } => todo!(), Type::Fn { .. } => todo!(),
Type::Var { .. } => todo!(), Type::Var { .. } => todo!(),
Type::Tuple { .. } => todo!(),
}; };
(index, name.clone()) (index, name.clone())
} }
@ -2628,6 +2640,7 @@ impl<'a> CodeGenerator<'a> {
Type::App { name, .. } => name, Type::App { name, .. } => name,
Type::Fn { .. } => todo!(), Type::Fn { .. } => todo!(),
Type::Var { .. } => todo!(), Type::Var { .. } => todo!(),
Type::Tuple { .. } => todo!(),
}; };
(index, name.clone()) (index, name.clone())
} }

View File

@ -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( pub fn spend(
datum: sample.Datum, datum: sample.Datum,
rdmr: Redeemer, rdmr: Redeemer,
ctx: spend.ScriptContext, ctx: spend.ScriptContext,
) -> Bool { ) -> Bool {
let x = Sell when [1, 2, 3] is {
let z = incrementor(0, 4) == 4 [a, b, ..] -> True
z [] -> False
}
} }