Fix record shorthand causing parsing ambiguity in if/else expressions.
Fixes #735.
This commit is contained in:
parent
1dea348a2e
commit
f379039efc
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}
|
|
@ -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",
|
||||
}
|
|
@ -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",
|
||||
},
|
||||
}
|
Loading…
Reference in New Issue