Support multi-clause patterns as syntactic sugar

And disable multi-patterns clauses. I was originally just controlling
  whether we did disable that from the parser but then I figured we
  could actually support multi-patterns clauses quite easily by simply
  desugaring a multi-pattern into multiple clauses.

  This is only a syntactic sugar, which means that the cost of writing
  that on-chain is as expensive as writing the fully expanded form; yet
  it seems like a useful shorthand; especially for short clause
  expressions.

  This commit however disables multi-pattern when clauses, which we do
  not support in the code-generation. Instead, one pattern on tuples for
  that.
This commit is contained in:
KtorZ 2023-03-16 22:42:07 +01:00 committed by Lucas
parent 660ca3fada
commit c113582404
9 changed files with 127 additions and 62 deletions

View File

@ -19,6 +19,7 @@
- **aiken-lang**: the compiler now provides better feedback for type holes (i.e. `_`) in type annotations
- **aiken-lang**: assignment and clause guard are now always formatted on a new line
- **aiken-lang**: unused let-bindings are now fully removed from generated code and discarded unused let-binding now raise a warning
- **aiken-lang**: support multi-clause patterns (only as a syntactic sugar)
## [v0.0.29] - 2023-MM-DD

View File

@ -936,6 +936,29 @@ impl TypedClause {
pub fn find_node(&self, byte_index: usize) -> Option<&TypedExpr> {
self.then.find_node(byte_index)
}
pub fn desugarize(self) -> Vec<Self> {
let mut alternative_patterns = self
.alternative_patterns
.into_iter()
.map(|pattern| Self {
location: self.location,
pattern,
alternative_patterns: vec![],
guard: self.guard.clone(),
then: self.then.clone(),
})
.collect::<Vec<_>>();
let mut clauses = vec![Self {
alternative_patterns: vec![],
..self
}];
clauses.append(&mut alternative_patterns);
clauses
}
}
pub type UntypedClauseGuard = ClauseGuard<()>;

View File

@ -921,15 +921,9 @@ pub fn expr_parser(
);
let when_clause_parser = pattern_parser()
.separated_by(just(Token::Comma))
.at_least(1)
.then(
just(Token::Vbar)
.ignore_then(
pattern_parser()
.separated_by(just(Token::Comma))
.at_least(1),
)
.ignore_then(pattern_parser().map(|pattern| vec![pattern]))
.repeated()
.or_not(),
)
@ -968,9 +962,9 @@ pub fn expr_parser(
}),
)))
.map_with_span(
|(((patterns, alternative_patterns_opt), guard), then), span| ast::UntypedClause {
|(((pattern, alternative_patterns_opt), guard), then), span| ast::UntypedClause {
location: span,
pattern: patterns,
pattern: vec![pattern],
alternative_patterns: alternative_patterns_opt.unwrap_or_default(),
guard,
then,

View File

@ -11,14 +11,14 @@ fn assert_definitions(code: &str, definitions: Vec<ast::UntypedDefinition>) {
let (module, _extra) = parser::module(code, ast::ModuleKind::Validator).unwrap();
assert_eq!(
module,
ast::UntypedModule {
docs: vec![],
kind: ast::ModuleKind::Validator,
name: "".to_string(),
type_info: (),
definitions,
}
},
module
)
}
@ -901,11 +901,10 @@ fn block() {
fn when() {
let code = indoc! {r#"
pub fn wow2(a: Int){
when a, b is {
1, 2 -> 3
1 | 4, 5 -> {
when a is {
2 -> 3
1 | 4 | 5 -> {
let amazing = 5
amazing
}
3 -> 9
@ -933,100 +932,88 @@ fn when() {
tipo: (),
}],
body: expr::UntypedExpr::When {
location: Span::new((), 23..138),
subjects: vec![
expr::UntypedExpr::Var {
location: Span::new((), 28..29),
name: "a".to_string(),
},
expr::UntypedExpr::Var {
location: Span::new((), 31..32),
name: "b".to_string(),
},
],
location: Span::new((), 23..132),
subjects: vec![expr::UntypedExpr::Var {
location: Span::new((), 28..29),
name: "a".to_string(),
}],
clauses: vec![
ast::Clause {
location: Span::new((), 42..51),
pattern: vec![
ast::Pattern::Int {
location: Span::new((), 42..43),
value: "1".to_string(),
},
ast::Pattern::Int {
location: Span::new((), 45..46),
value: "2".to_string(),
},
],
location: Span::new((), 39..45),
pattern: vec![ast::Pattern::Int {
location: Span::new((), 39..40),
value: "2".to_string(),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Int {
location: Span::new((), 50..51),
location: Span::new((), 44..45),
value: "3".to_string(),
},
},
ast::Clause {
location: Span::new((), 56..112),
location: Span::new((), 50..106),
pattern: vec![ast::Pattern::Int {
location: Span::new((), 56..57),
location: Span::new((), 50..51),
value: "1".to_string(),
}],
alternative_patterns: vec![vec![
ast::Pattern::Int {
location: Span::new((), 60..61),
alternative_patterns: vec![
vec![ast::Pattern::Int {
location: Span::new((), 54..55),
value: "4".to_string(),
},
ast::Pattern::Int {
location: Span::new((), 63..64),
}],
vec![ast::Pattern::Int {
location: Span::new((), 58..59),
value: "5".to_string(),
},
]],
}],
],
guard: None,
then: expr::UntypedExpr::Sequence {
location: Span::new((), 76..106),
location: Span::new((), 71..100),
expressions: vec![
expr::UntypedExpr::Assignment {
location: Span::new((), 76..91),
location: Span::new((), 71..86),
value: Box::new(expr::UntypedExpr::Int {
location: Span::new((), 90..91),
location: Span::new((), 85..86),
value: "5".to_string(),
}),
pattern: ast::Pattern::Var {
location: Span::new((), 80..87),
location: Span::new((), 75..82),
name: "amazing".to_string(),
},
kind: ast::AssignmentKind::Let,
annotation: None,
},
expr::UntypedExpr::Var {
location: Span::new((), 99..106),
location: Span::new((), 93..100),
name: "amazing".to_string(),
},
],
},
},
ast::Clause {
location: Span::new((), 117..123),
location: Span::new((), 111..117),
pattern: vec![ast::Pattern::Int {
location: Span::new((), 117..118),
location: Span::new((), 111..112),
value: "3".to_string(),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Int {
location: Span::new((), 122..123),
location: Span::new((), 116..117),
value: "9".to_string(),
},
},
ast::Clause {
location: Span::new((), 128..134),
location: Span::new((), 122..128),
pattern: vec![ast::Pattern::Discard {
name: "_".to_string(),
location: Span::new((), 128..129),
location: Span::new((), 122..123),
}],
alternative_patterns: vec![],
guard: None,
then: expr::UntypedExpr::Int {
location: Span::new((), 133..134),
location: Span::new((), 127..128),
value: "4".to_string(),
},
},
@ -1038,7 +1025,7 @@ fn when() {
public: true,
return_annotation: None,
return_type: (),
end_position: 139,
end_position: 133,
})],
)
}

View File

@ -1888,7 +1888,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let mut typed_subjects = Vec::with_capacity(subjects_count);
let mut subject_types = Vec::with_capacity(subjects_count);
let mut typed_clauses = Vec::with_capacity(clauses.len());
let mut typed_clauses = Vec::new();
let return_type = self.new_unbound_var();
@ -1911,7 +1911,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
)
.map_err(|e| e.case_clause_mismatch())?;
typed_clauses.push(typed_clause);
typed_clauses.append(&mut typed_clause.desugarize());
}
if let Err(unmatched) =

View File

@ -0,0 +1,5 @@
# This file was generated by Aiken
# You typically do not need to edit this file
requirements = []
packages = []

View File

@ -0,0 +1,2 @@
name = "aiken-lang/acceptance_test_078"
version = "0.0.0"

View File

@ -0,0 +1,45 @@
type DayOfTheWeek {
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
}
fn is_work(day: DayOfTheWeek) {
when day is {
Tuesday | Wednesday | Thursday | Friday | Saturday ->
True
_ ->
False
}
}
test is_work_1() {
is_work(Thursday)
}
test is_work_2() {
!is_work(Monday)
}
fn is_happy_hour(day: DayOfTheWeek, current_time: Int) {
when day is {
Monday | Sunday ->
True
Tuesday | Wednesday | Thursday | Friday | Saturday if current_time > 18 ->
True
_ ->
False
}
}
test is_happy_hour_1() {
is_happy_hour(Sunday, 14)
}
test is_happy_hour_2() {
is_happy_hour(Friday, 22)
}

View File

@ -0,0 +1,8 @@
{
"preamble": {
"title": "aiken-lang/acceptance_test_078",
"version": "0.0.0",
"plutusVersion": "v2"
},
"validators": []
}