Implement parser & formater for 'TraceIfFalse'

Interestingly enough, chumsky seems to fail when given a 'choice' with
  more than 25 elements. That's why this commit groups together some of
  the choices as another nested 'choice'.
This commit is contained in:
KtorZ 2023-02-16 13:51:37 +01:00 committed by Lucas
parent 60390fe4f0
commit 6a50bde666
5 changed files with 80 additions and 7 deletions

View File

@ -1280,6 +1280,16 @@ pub fn expr_parser(
}, },
}); });
let debug = chained.then(just(Token::Question).or_not()).map_with_span(
|(value, token), location| match token {
Some(_) => expr::UntypedExpr::TraceIfFalse {
value: Box::new(value),
location,
},
None => value,
},
);
// Negate // Negate
let op = choice(( let op = choice((
just(Token::Bang).to(UnOp::Not), just(Token::Bang).to(UnOp::Not),
@ -1289,7 +1299,7 @@ pub fn expr_parser(
let unary = op let unary = op
.map_with_span(|op, span| (op, span)) .map_with_span(|op, span| (op, span))
.repeated() .repeated()
.then(chained) .then(debug)
.foldr(|(un_op, span), value| expr::UntypedExpr::UnOp { .foldr(|(un_op, span), value| expr::UntypedExpr::UnOp {
op: un_op, op: un_op,
location: span.union(value.location()), location: span.union(value.location()),

View File

@ -35,10 +35,13 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
just('.').to(Token::Dot), just('.').to(Token::Dot),
just("!=").to(Token::NotEqual), just("!=").to(Token::NotEqual),
just('!').to(Token::Bang), just('!').to(Token::Bang),
just("<=").to(Token::LessEqual), just('?').to(Token::Question),
just('<').to(Token::Less), choice((
just(">=").to(Token::GreaterEqual), just("<=").to(Token::LessEqual),
just('>').to(Token::Greater), just('<').to(Token::Less),
just(">=").to(Token::GreaterEqual),
just('>').to(Token::Greater),
)),
just('+').to(Token::Plus), just('+').to(Token::Plus),
just("->").to(Token::RArrow), just("->").to(Token::RArrow),
just('-').to(Token::Minus), just('-').to(Token::Minus),

View File

@ -39,8 +39,9 @@ pub enum Token {
// Other Punctuation // Other Punctuation
Colon, Colon,
Comma, Comma,
Hash, // '#' Hash, // '#'
Bang, // '!' Bang, // '!'
Question, // '?'
Equal, Equal,
EqualEqual, // '==' EqualEqual, // '=='
NotEqual, // '!=' NotEqual, // '!='
@ -125,6 +126,7 @@ impl fmt::Display for Token {
Token::Hash => "#", Token::Hash => "#",
Token::Bang => "!", Token::Bang => "!",
Token::Equal => "=", Token::Equal => "=",
Token::Question => "?",
Token::EqualEqual => "==", Token::EqualEqual => "==",
Token::NotEqual => "!=", Token::NotEqual => "!=",
Token::Vbar => "|", Token::Vbar => "|",

View File

@ -255,3 +255,46 @@ fn trace_non_strings() {
Err((_, Error::CouldNotUnify { .. })) Err((_, Error::CouldNotUnify { .. }))
)) ))
} }
#[test]
fn trace_if_false_ok() {
let source_code = r#"
fn or(a: Bool, b: Bool) {
(a || b)?
}
test foo() {
or(True, False)?
}
test bar() {
let must_be_signed = True
must_be_signed?
}
"#;
assert!(check(parse(source_code)).is_ok())
}
#[test]
fn trace_if_false_ko() {
let source_code = r#"
fn add(a: Int, b: Int) {
(a + b)?
}
test foo() {
add(14, 42) == 12
}
test bar() {
let must_be_signed = #"FF00"
must_be_signed? == #"FF00"
}
"#;
assert!(matches!(
check(parse(source_code)),
Err((_, Error::CouldNotUnify { .. }))
))
}

View File

@ -400,3 +400,18 @@ fn format_trace_todo_error() {
assert_fmt(src, src); assert_fmt(src, src);
} }
#[test]
fn test_trace_if_false() {
let src = indoc! {r#"
fn foo() {
my_expression?
}
fn bar() {
(True && False)? || foo()?
}
"#};
assert_fmt(src, src);
}