Remove clause guards.

Closes #886.
This commit is contained in:
KtorZ
2024-08-01 16:14:56 +02:00
committed by Kasey
parent b28d4a6e9f
commit bf5a406ffb
65 changed files with 170 additions and 1492 deletions

View File

@@ -35,7 +35,7 @@ use std::collections::HashSet;
)]
pub struct ParseError {
pub kind: ErrorKind,
#[label]
#[label("{}", .label.unwrap_or_default())]
pub span: Span,
#[allow(dead_code)]
while_parsing: Option<(Span, &'static str)>,
@@ -83,13 +83,13 @@ impl ParseError {
}
}
pub fn invalid_when_clause_guard(span: Span) -> Self {
pub fn deprecated_when_clause_guard(span: Span) -> Self {
Self {
kind: ErrorKind::InvalidWhenClause,
kind: ErrorKind::DeprecatedWhenClause,
span,
while_parsing: None,
expected: HashSet::new(),
label: Some("invalid clause guard"),
label: Some("deprecated"),
}
}
@@ -249,35 +249,17 @@ pub enum ErrorKind {
}))]
MalformedBase16StringLiteral,
#[error("I came across a bytearray declared using two different notations")]
#[error("I came across a bytearray declared using two different notations.")]
#[diagnostic(url("https://aiken-lang.org/language-tour/primitive-types#bytearray"))]
#[diagnostic(help("Either use decimal or hexadecimal notation, but don't mix them."))]
HybridNotationInByteArray,
#[error("I failed to understand a when clause guard.")]
#[diagnostic(url(
"https://aiken-lang.org/language-tour/control-flow#checking-equality-and-ordering-in-patterns"
))]
#[error("I found a now-deprecated clause guard in a when/is expression.")]
#[diagnostic(help("{}", formatdoc! {
r#"Clause guards are not as capable as standard expressions. While you can combine multiple clauses using '{operator_or}' and '{operator_and}', you can't do any arithmetic in there. They are mainly meant to compare pattern variables to some known constants using simple binary operators.
For example, the following clauses are well-formed:
{good} (x, _) if x == 10 -> ...
{good} (_, y) if y > 0 && y < 10 -> ...
{good} (x, y) if x && (y > 0 || y < 10) -> ...
However, those aren't:
{bad} (x, _) if x % 3 == 0 -> ...
{bad} (x, y) if x + y > 42 -> ...
r#"Clause guards have been removed from Aiken. They were underused, considered potentially harmful and created needless complexity in the compiler. If you were using clause guards, our apologies, but you can now update your code and move the clause guards patterns inside a nested if/else expression.
"#
, operator_or = "||".if_supports_color(Stdout, |s| s.yellow())
, operator_and = "&&".if_supports_color(Stdout, |s| s.yellow())
, good = "✔️".if_supports_color(Stdout, |s| s.green())
, bad = "✖️".if_supports_color(Stdout, |s| s.red())
}))]
InvalidWhenClause,
DeprecatedWhenClause,
}
fn fmt_curve_type(curve: &CurveType) -> String {

View File

@@ -1,45 +1,41 @@
use chumsky::prelude::*;
use vec1::vec1;
use super::guard;
use crate::{
ast,
expr::UntypedExpr,
parser::{error::ParseError, pattern, token::Token},
};
use super::guard;
use chumsky::prelude::*;
use vec1::vec1;
pub fn parser(
expression: Recursive<'_, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, ast::UntypedClause, Error = ParseError> + '_ {
pattern()
.then(just(Token::Vbar).ignore_then(pattern()).repeated().or_not())
.then(choice((
just(Token::If)
.ignore_then(guard())
.or_not()
.then_ignore(just(Token::RArrow)),
just(Token::If)
.ignore_then(take_until(just(Token::RArrow)))
.validate(|_value, span, emit| {
emit(ParseError::invalid_when_clause_guard(span));
None
}),
)))
.then(choice((just(Token::If)
.ignore_then(guard())
.or_not()
.then_ignore(just(Token::RArrow)),)))
// TODO: add hint "Did you mean to wrap a multi line clause in curly braces?"
.then(expression)
.map_with_span(
|(((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,
.validate(
|(((pattern, alternative_patterns_opt), guard), then), span, emit| {
if guard.is_some() {
emit(ParseError::deprecated_when_clause_guard(span));
}
(pattern, alternative_patterns_opt, then)
},
)
.map_with_span(|(pattern, alternative_patterns_opt, then), span| {
let mut patterns = vec1![pattern];
patterns.append(&mut alternative_patterns_opt.unwrap_or_default());
ast::UntypedClause {
location: span,
patterns,
then,
}
})
}
#[cfg(test)]

View File

@@ -1,9 +1,8 @@
use chumsky::prelude::*;
use crate::{
ast,
parser::{definition, error::ParseError, token::Token},
};
use chumsky::prelude::*;
pub fn parser() -> impl Parser<Token, ast::UntypedClauseGuard, Error = ParseError> {
recursive(|expression| {
@@ -11,13 +10,9 @@ pub fn parser() -> impl Parser<Token, ast::UntypedClauseGuard, Error = ParseErro
Token::Name { name } => name,
Token::UpName { name } => name,
}
.map_with_span(|name, span| ast::ClauseGuard::Var {
name,
tipo: (),
location: span,
});
.map_with_span(|_name, _span| ast::UntypedClauseGuard {});
let constant_parser = definition::constant::value().map(ast::ClauseGuard::Constant);
let constant_parser = definition::constant::value().map(|_| ast::UntypedClauseGuard {});
let block_parser = expression
.clone()
@@ -31,10 +26,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedClauseGuard, Error = ParseErro
.map_with_span(|op, span| (op, span))
.repeated()
.then(leaf_parser)
.foldr(|(_, span), value| ast::ClauseGuard::Not {
location: span.union(value.location()),
value: Box::new(value),
})
.foldr(|(_, _span), _value| ast::UntypedClauseGuard {})
.boxed();
let comparison_op = choice((
@@ -49,74 +41,19 @@ pub fn parser() -> impl Parser<Token, ast::UntypedClauseGuard, Error = ParseErro
let comparison = unary
.clone()
.then(comparison_op.then(unary).repeated())
.foldl(|left, (op, right)| {
let location = left.location().union(right.location());
let left = Box::new(left);
let right = Box::new(right);
match op {
ast::BinOp::Eq => ast::ClauseGuard::Equals {
location,
left,
right,
},
ast::BinOp::NotEq => ast::ClauseGuard::NotEquals {
location,
left,
right,
},
ast::BinOp::LtInt => ast::ClauseGuard::LtInt {
location,
left,
right,
},
ast::BinOp::GtInt => ast::ClauseGuard::GtInt {
location,
left,
right,
},
ast::BinOp::LtEqInt => ast::ClauseGuard::LtEqInt {
location,
left,
right,
},
ast::BinOp::GtEqInt => ast::ClauseGuard::GtEqInt {
location,
left,
right,
},
_ => unreachable!(),
}
})
.foldl(|_left, (_op, _right)| ast::UntypedClauseGuard {})
.boxed();
let and_op = just(Token::AmperAmper);
let conjunction = comparison
.clone()
.then(and_op.then(comparison).repeated())
.foldl(|left, (_tok, right)| {
let location = left.location().union(right.location());
let left = Box::new(left);
let right = Box::new(right);
ast::ClauseGuard::And {
location,
left,
right,
}
});
.foldl(|_left, (_tok, _right)| ast::UntypedClauseGuard {});
let or_op = just(Token::VbarVbar);
conjunction
.clone()
.then(or_op.then(conjunction).repeated())
.foldl(|left, (_tok, right)| {
let location = left.location().union(right.location());
let left = Box::new(left);
let right = Box::new(right);
ast::ClauseGuard::Or {
location,
left,
right,
}
})
.foldl(|_left, (_tok, _right)| ast::UntypedClauseGuard {})
})
}

View File

@@ -3,13 +3,12 @@ use chumsky::prelude::*;
mod clause;
mod guard;
pub use clause::parser as clause;
pub use guard::parser as guard;
use crate::{
expr::UntypedExpr,
parser::{error::ParseError, token::Token},
};
pub use clause::parser as clause;
pub use guard::parser as guard;
pub fn parser(
expression: Recursive<'_, Token, UntypedExpr, ParseError>,
@@ -38,7 +37,6 @@ mod tests {
assert_expr!(
r#"
when a is {
2 if x > 1 -> 3
1 | 4 | 5 -> {
let amazing = 5
amazing
@@ -49,4 +47,16 @@ mod tests {
"#
);
}
#[test]
fn when_guard_deprecation() {
assert_expr!(
r#"
when a is {
2 if x > 1 -> 3
_ -> 1
}
"#
);
}
}

View File

@@ -1,85 +1,46 @@
---
source: crates/aiken-lang/src/parser/expr/when/mod.rs
description: "Code:\n\nwhen a is {\n 2 if x > 1 -> 3\n 1 | 4 | 5 -> {\n let amazing = 5\n amazing\n }\n 3 -> 9\n _ -> 4\n}\n"
description: "Code:\n\nwhen a is {\n 1 | 4 | 5 -> {\n let amazing = 5\n amazing\n }\n 3 -> 9\n _ -> 4\n}\n"
---
When {
location: 0..102,
location: 0..84,
subject: Var {
location: 5..6,
name: "a",
},
clauses: [
UntypedClause {
location: 14..29,
location: 14..64,
patterns: [
Int {
location: 14..15,
value: "2",
base: Decimal {
numeric_underscore: false,
},
},
],
guard: Some(
GtInt {
location: 19..24,
left: Var {
location: 19..20,
tipo: (),
name: "x",
},
right: Constant(
Int {
location: 23..24,
value: "1",
base: Decimal {
numeric_underscore: false,
},
},
),
},
),
then: UInt {
location: 28..29,
value: "3",
base: Decimal {
numeric_underscore: false,
},
},
},
UntypedClause {
location: 32..82,
patterns: [
Int {
location: 32..33,
value: "1",
base: Decimal {
numeric_underscore: false,
},
},
Int {
location: 36..37,
location: 18..19,
value: "4",
base: Decimal {
numeric_underscore: false,
},
},
Int {
location: 40..41,
location: 22..23,
value: "5",
base: Decimal {
numeric_underscore: false,
},
},
],
guard: None,
then: Sequence {
location: 51..78,
location: 33..60,
expressions: [
Assignment {
location: 51..66,
location: 33..48,
value: UInt {
location: 65..66,
location: 47..48,
value: "5",
base: Decimal {
numeric_underscore: false,
@@ -88,11 +49,11 @@ When {
patterns: [
AssignmentPattern {
pattern: Var {
location: 55..62,
location: 37..44,
name: "amazing",
},
annotation: None,
location: 55..62,
location: 37..44,
},
],
kind: Let {
@@ -100,26 +61,25 @@ When {
},
},
Var {
location: 71..78,
location: 53..60,
name: "amazing",
},
],
},
},
UntypedClause {
location: 85..91,
location: 67..73,
patterns: [
Int {
location: 85..86,
location: 67..68,
value: "3",
base: Decimal {
numeric_underscore: false,
},
},
],
guard: None,
then: UInt {
location: 90..91,
location: 72..73,
value: "9",
base: Decimal {
numeric_underscore: false,
@@ -127,16 +87,15 @@ When {
},
},
UntypedClause {
location: 94..100,
location: 76..82,
patterns: [
Discard {
name: "_",
location: 94..95,
location: 76..77,
},
],
guard: None,
then: UInt {
location: 99..100,
location: 81..82,
value: "4",
base: Decimal {
numeric_underscore: false,

View File

@@ -25,7 +25,6 @@ When {
tipo: (),
},
],
guard: None,
then: Trace {
kind: Todo,
location: 28..32,
@@ -55,7 +54,6 @@ When {
tipo: (),
},
],
guard: None,
then: Trace {
kind: Todo,
location: 47..51,

View File

@@ -25,7 +25,6 @@ When {
tipo: (),
},
],
guard: None,
then: ErrorTerm {
location: 28..32,
},

View File

@@ -25,7 +25,6 @@ When {
tipo: (),
},
],
guard: None,
then: Var {
location: 28..32,
name: "True",
@@ -47,7 +46,6 @@ When {
tipo: (),
},
],
guard: None,
then: Trace {
kind: Todo,
location: 47..68,

View File

@@ -0,0 +1,15 @@
---
source: crates/aiken-lang/src/parser/expr/when/mod.rs
description: "Invalid code (parse error):\n\nwhen a is {\n 2 if x > 1 -> 3\n _ -> 1\n}\n"
---
[
ParseError {
kind: DeprecatedWhenClause,
span: 14..29,
while_parsing: None,
expected: {},
label: Some(
"deprecated",
),
},
]

View File

@@ -20,7 +20,6 @@ When {
},
},
],
guard: None,
then: Var {
location: 22..26,
name: "True",

View File

@@ -20,7 +20,6 @@ When {
},
},
],
guard: None,
then: UnOp {
op: Negate,
location: 22..25,
@@ -44,7 +43,6 @@ When {
},
},
],
guard: None,
then: UInt {
location: 35..37,
value: "14",