@@ -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 {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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 {})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -25,7 +25,6 @@ When {
|
||||
tipo: (),
|
||||
},
|
||||
],
|
||||
guard: None,
|
||||
then: ErrorTerm {
|
||||
location: 28..32,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
),
|
||||
},
|
||||
]
|
||||
@@ -20,7 +20,6 @@ When {
|
||||
},
|
||||
},
|
||||
],
|
||||
guard: None,
|
||||
then: Var {
|
||||
location: 22..26,
|
||||
name: "True",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user