Fix record shorthand causing parsing ambiguity in if/else expressions.

Fixes #735.
This commit is contained in:
KtorZ 2023-09-15 09:40:40 +02:00
parent 1dea348a2e
commit f379039efc
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
7 changed files with 182 additions and 2 deletions

View File

@ -13,6 +13,7 @@
- **uplc**: uplc `Constant::Data` formatting
- **aiken-lang**: empty records properly parse as record sugar
- **aiken-lang**: escape sequences are now properly preserved after formatting
- **aiken-lang**: fixed parser ambiguity when using record constructor in if conditions followed by single-line var expressions #735
- **aiken-project**: when a module name has a hyphen we should behave like rust and force an underscore
## v1.0.16-alpha - 2023-08-24

View File

@ -24,15 +24,24 @@ mod tests {
use crate::assert_expr;
#[test]
fn block_basic() {
fn block_let() {
assert_expr!(
r#"
let b = {
let x = 4
x + 5
}
"#
);
}
#[test]
fn block_single() {
assert_expr!(
r#"{
foo
}
"#
);
}
}

View File

@ -71,4 +71,19 @@ mod tests {
"#
);
}
#[test]
fn if_else_ambiguous_record() {
assert_expr!(
r#"
if ec1 == Infinity {
ec2
} else if ec1 == Foo { foo } {
ec1
} else {
Infinity
}
"#
);
}
}

View File

@ -72,6 +72,38 @@ pub fn parser(
},
),
))
// NOTE: There's an ambiguity when the record shorthand syntax is used
// from within an if-else statement in the case of single-variable if-branch.
//
// For example, imagine the following:
//
// ```
// if season == Summer {
// foo
// } else {
// bar
// }
// ```
//
// Without that next odd parser combinator, the parser would parse:
//
// ```
// if season == Summer { foo }
// else {
// bar
// }
// ```
//
// And immediately choke on the next `else` because the if-branch body has
// already been consumed and interpreted as a record definition. So the next
// combinator ensures that we give priority back to an if-then statement rather
// than to the record definition.
.then_ignore(
just(Token::RightBrace)
.ignore_then(just(Token::Else))
.not()
.rewind(),
)
.map(|(value, name)| ast::CallArg {
location: value.location(),
value,

View File

@ -0,0 +1,49 @@
---
source: crates/aiken-lang/src/parser/expr/block.rs
description: "Code:\n\nlet b = {\n let x = 4\n x + 5\n}\n"
---
Assignment {
location: 0..31,
value: Sequence {
location: 12..29,
expressions: [
Assignment {
location: 12..21,
value: UInt {
location: 20..21,
value: "4",
base: Decimal {
numeric_underscore: false,
},
},
pattern: Var {
location: 16..17,
name: "x",
},
kind: Let,
annotation: None,
},
BinOp {
location: 24..29,
name: AddInt,
left: Var {
location: 24..25,
name: "x",
},
right: UInt {
location: 28..29,
value: "5",
base: Decimal {
numeric_underscore: false,
},
},
},
],
},
pattern: Var {
location: 4..5,
name: "b",
},
kind: Let,
annotation: None,
}

View File

@ -0,0 +1,8 @@
---
source: crates/aiken-lang/src/parser/expr/block.rs
description: "Code:\n\n{\nfoo\n}\n"
---
Var {
location: 2..5,
name: "foo",
}

View File

@ -0,0 +1,66 @@
---
source: crates/aiken-lang/src/parser/expr/if_else.rs
description: "Code:\n\nif ec1 == Infinity {\n ec2\n} else if ec1 == Foo { foo } {\n ec1\n} else {\n Infinity\n}\n"
---
If {
location: 0..85,
branches: [
IfBranch {
condition: BinOp {
location: 3..18,
name: Eq,
left: Var {
location: 3..6,
name: "ec1",
},
right: Var {
location: 10..18,
name: "Infinity",
},
},
body: Var {
location: 23..26,
name: "ec2",
},
location: 3..28,
},
IfBranch {
condition: BinOp {
location: 37..55,
name: Eq,
left: Var {
location: 37..40,
name: "ec1",
},
right: Call {
arguments: [
CallArg {
label: Some(
"foo",
),
location: 50..53,
value: Var {
location: 50..53,
name: "foo",
},
},
],
fun: Var {
location: 44..47,
name: "Foo",
},
location: 44..55,
},
},
body: Var {
location: 60..63,
name: "ec1",
},
location: 37..65,
},
],
final_else: Var {
location: 75..83,
name: "Infinity",
},
}