feat: expect boolean sugar
This commit is contained in:
parent
d009358266
commit
1b8e94fe32
|
@ -2,6 +2,10 @@
|
|||
|
||||
## v1.0.13-alpha - unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- **aiken-lang**: `expect foo == bar` desugars into `expect True = foo == bar`
|
||||
|
||||
### Fixed
|
||||
|
||||
- **aiken-lang**: fail, todo, and trace had issues with sequences and expressions
|
||||
|
|
|
@ -647,6 +647,13 @@ impl<'comments> Formatter<'comments> {
|
|||
AssignmentKind::Expect => "expect",
|
||||
};
|
||||
|
||||
match pattern {
|
||||
UntypedPattern::Constructor {
|
||||
name, module: None, ..
|
||||
} if name == "True" && annotation.is_none() && kind.is_expect() => {
|
||||
keyword.to_doc().append(self.case_clause_value(value))
|
||||
}
|
||||
_ => {
|
||||
let pattern = self.pattern(pattern);
|
||||
|
||||
let annotation = annotation
|
||||
|
@ -655,10 +662,13 @@ impl<'comments> Formatter<'comments> {
|
|||
|
||||
keyword
|
||||
.to_doc()
|
||||
.append(" ")
|
||||
.append(pattern.append(annotation).group())
|
||||
.append(" =")
|
||||
.append(self.case_clause_value(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytearray<'a>(
|
||||
&mut self,
|
||||
|
|
|
@ -9,25 +9,7 @@ use crate::{
|
|||
pub fn let_(
|
||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
assignment(r, ast::AssignmentKind::Let)
|
||||
}
|
||||
|
||||
pub fn expect(
|
||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
assignment(r, ast::AssignmentKind::Expect)
|
||||
}
|
||||
|
||||
fn assignment(
|
||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
kind: ast::AssignmentKind,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
let keyword = match kind {
|
||||
ast::AssignmentKind::Let => Token::Let,
|
||||
ast::AssignmentKind::Expect => Token::Expect,
|
||||
};
|
||||
|
||||
just(keyword)
|
||||
just(Token::Let)
|
||||
.ignore_then(pattern())
|
||||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||
.then_ignore(just(Token::Equal))
|
||||
|
@ -37,12 +19,50 @@ fn assignment(
|
|||
location: span,
|
||||
value: Box::new(value),
|
||||
pattern,
|
||||
kind,
|
||||
kind: ast::AssignmentKind::Let,
|
||||
annotation,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn expect(
|
||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
just(Token::Expect)
|
||||
.ignore_then(
|
||||
pattern()
|
||||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||
.then_ignore(just(Token::Equal))
|
||||
.or_not(),
|
||||
)
|
||||
.then(r.clone())
|
||||
.map_with_span(move |(opt_pattern, value), span| {
|
||||
let (pattern, annotation) = opt_pattern.unwrap_or_else(|| {
|
||||
(
|
||||
ast::UntypedPattern::Constructor {
|
||||
is_record: false,
|
||||
location: span,
|
||||
name: "True".to_string(),
|
||||
arguments: vec![],
|
||||
module: None,
|
||||
constructor: (),
|
||||
with_spread: false,
|
||||
tipo: (),
|
||||
},
|
||||
None,
|
||||
)
|
||||
});
|
||||
|
||||
UntypedExpr::Assignment {
|
||||
location: span,
|
||||
value: Box::new(value),
|
||||
pattern,
|
||||
kind: ast::AssignmentKind::Expect,
|
||||
annotation,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::assert_expr;
|
||||
|
@ -56,4 +76,9 @@ mod tests {
|
|||
fn expect() {
|
||||
assert_expr!("expect Some(x) = something.field");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expect_bool_sugar() {
|
||||
assert_expr!("expect something.field == wow");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/expr/assignment.rs
|
||||
description: "Code:\n\nexpect something.field == wow"
|
||||
---
|
||||
Assignment {
|
||||
location: 0..29,
|
||||
value: BinOp {
|
||||
location: 7..29,
|
||||
name: Eq,
|
||||
left: FieldAccess {
|
||||
location: 7..22,
|
||||
label: "field",
|
||||
container: Var {
|
||||
location: 7..16,
|
||||
name: "something",
|
||||
},
|
||||
},
|
||||
right: Var {
|
||||
location: 26..29,
|
||||
name: "wow",
|
||||
},
|
||||
},
|
||||
pattern: Constructor {
|
||||
is_record: false,
|
||||
location: 0..29,
|
||||
name: "True",
|
||||
arguments: [],
|
||||
module: None,
|
||||
constructor: (),
|
||||
with_spread: false,
|
||||
tipo: (),
|
||||
},
|
||||
kind: Expect,
|
||||
annotation: None,
|
||||
}
|
|
@ -83,3 +83,18 @@ pub(crate) fn args(
|
|||
.unwrap_or_else(|| (vec![], false, false))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::assert_pattern;
|
||||
|
||||
#[test]
|
||||
fn constructor_basic() {
|
||||
assert_pattern!("True");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constructor_module_select() {
|
||||
assert_pattern!("module.Foo");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/pattern/constructor.rs
|
||||
description: "Code:\n\nTrue"
|
||||
---
|
||||
Constructor {
|
||||
is_record: false,
|
||||
location: 0..4,
|
||||
name: "True",
|
||||
arguments: [],
|
||||
module: None,
|
||||
constructor: (),
|
||||
with_spread: false,
|
||||
tipo: (),
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/pattern/constructor.rs
|
||||
description: "Code:\n\nmodule.Foo"
|
||||
---
|
||||
Constructor {
|
||||
is_record: false,
|
||||
location: 0..10,
|
||||
name: "Foo",
|
||||
arguments: [],
|
||||
module: Some(
|
||||
"module",
|
||||
),
|
||||
constructor: (),
|
||||
with_spread: false,
|
||||
tipo: (),
|
||||
}
|
|
@ -61,6 +61,27 @@ macro_rules! assert_annotation {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_pattern {
|
||||
($code:expr) => {
|
||||
use chumsky::Parser;
|
||||
|
||||
let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
|
||||
|
||||
let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
|
||||
|
||||
let result = $crate::parser::pattern().parse(stream).unwrap();
|
||||
|
||||
insta::with_settings!({
|
||||
description => concat!("Code:\n\n", indoc::indoc! { $code }),
|
||||
prepend_module_to_snapshot => false,
|
||||
omit_expression => true
|
||||
}, {
|
||||
insta::assert_debug_snapshot!(result);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_module {
|
||||
($code:expr) => {
|
||||
|
|
|
@ -154,6 +154,33 @@ fn multi_validator_warning() {
|
|||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expect_sugar_correct_type() {
|
||||
let source_code = r#"
|
||||
fn foo() {
|
||||
expect 1 == 1
|
||||
2
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(matches!(check(parse(source_code)), Ok(_)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expect_sugar_incorrect_type() {
|
||||
let source_code = r#"
|
||||
fn foo() {
|
||||
expect 1
|
||||
2
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(matches!(
|
||||
dbg!(check(parse(source_code))),
|
||||
Err((_, Error::CouldNotUnify { .. }))
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anonymous_function_scoping() {
|
||||
let source_code = r#"
|
||||
|
|
Loading…
Reference in New Issue