diff --git a/crates/aiken-lang/src/parser/error.rs b/crates/aiken-lang/src/parser/error.rs index 4c8ff215..9308ca42 100644 --- a/crates/aiken-lang/src/parser/error.rs +++ b/crates/aiken-lang/src/parser/error.rs @@ -30,7 +30,7 @@ impl ParseError { pub fn invalid_assignment_right_hand_side(span: Span) -> Self { Self { - kind: ErrorKind::InvalidAssignmentRightHandSide, + kind: ErrorKind::UnfinishedAssignmentRightHandSide, span, while_parsing: None, expected: HashSet::new(), @@ -173,7 +173,7 @@ pub enum ErrorKind { hint: Option, }, - #[error("I discovered an invalid assignment.")] + #[error("I discovered an unfinished assignment.")] #[diagnostic( help( "{} and {} bindings must be followed by a valid expression.", @@ -181,7 +181,7 @@ pub enum ErrorKind { "expect".if_supports_color(Stdout, |s| s.yellow()), ), )] - InvalidAssignmentRightHandSide, + UnfinishedAssignmentRightHandSide, #[error("I tripped over a {}", fmt_curve_type(.curve))] PointNotOnCurve { curve: CurveType }, diff --git a/crates/aiken-lang/src/parser/expr/assignment.rs b/crates/aiken-lang/src/parser/expr/assignment.rs index 2ebc6887..30f8fbf5 100644 --- a/crates/aiken-lang/src/parser/expr/assignment.rs +++ b/crates/aiken-lang/src/parser/expr/assignment.rs @@ -94,4 +94,42 @@ mod tests { fn expect_trace_if_false() { assert_expr!("expect foo?"); } + + #[test] + fn expect_unfinished_let() { + assert_expr!( + " + let a = + // foo + let b = 42 + " + ); + } + + #[test] + fn expect_let_in_let() { + assert_expr!("let a = { let b = 42 }"); + } + + #[test] + fn expect_let_in_let_return() { + assert_expr!( + " + let a = { + let b = 42 + b + } + " + ); + } + + #[test] + fn expect_let_in_let_parens() { + assert_expr!("let a = ( let b = 42 )"); + } + + #[test] + fn expect_expect_let() { + assert_expr!("expect { let a = 42 } = foo"); + } } diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_expect_let.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_expect_let.snap new file mode 100644 index 00000000..2f2b1431 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_expect_let.snap @@ -0,0 +1,40 @@ +--- +source: crates/aiken-lang/src/parser/expr/assignment.rs +description: "Code:\n\nexpect { let a = 42 } = foo" +--- +Assignment { + location: 0..21, + value: Sequence { + location: 7..21, + expressions: [ + Assignment { + location: 9..19, + value: UInt { + location: 17..19, + value: "42", + base: Decimal { + numeric_underscore: false, + }, + }, + pattern: Var { + location: 13..14, + name: "a", + }, + kind: Let, + annotation: None, + }, + ], + }, + pattern: Constructor { + is_record: false, + location: 0..21, + name: "True", + arguments: [], + module: None, + constructor: (), + with_spread: false, + tipo: (), + }, + kind: Expect, + annotation: None, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let.snap new file mode 100644 index 00000000..feaa148b --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let.snap @@ -0,0 +1,34 @@ +--- +source: crates/aiken-lang/src/parser/expr/assignment.rs +description: "Code:\n\nlet a = { let b = 42 }" +--- +Assignment { + location: 0..22, + value: Sequence { + location: 8..22, + expressions: [ + Assignment { + location: 10..20, + value: UInt { + location: 18..20, + value: "42", + base: Decimal { + numeric_underscore: false, + }, + }, + pattern: Var { + location: 14..15, + name: "b", + }, + kind: Let, + annotation: None, + }, + ], + }, + pattern: Var { + location: 4..5, + name: "a", + }, + kind: Let, + annotation: None, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_parens.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_parens.snap new file mode 100644 index 00000000..250c033a --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_parens.snap @@ -0,0 +1,15 @@ +--- +source: crates/aiken-lang/src/parser/expr/assignment.rs +description: "Invalid code (parse error):\n\nlet a = ( let b = 42 )" +--- +[ + ParseError { + kind: UnfinishedAssignmentRightHandSide, + span: 0..22, + while_parsing: None, + expected: {}, + label: Some( + "invalid assignment right-hand side", + ), + }, +] diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_return.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_return.snap new file mode 100644 index 00000000..0d11cf5f --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_return.snap @@ -0,0 +1,38 @@ +--- +source: crates/aiken-lang/src/parser/expr/assignment.rs +description: "Code:\n\nlet a = {\n let b = 42\n b\n}\n" +--- +Assignment { + location: 0..28, + value: Sequence { + location: 12..26, + expressions: [ + Assignment { + location: 12..22, + value: UInt { + location: 20..22, + value: "42", + base: Decimal { + numeric_underscore: false, + }, + }, + pattern: Var { + location: 16..17, + name: "b", + }, + kind: Let, + annotation: None, + }, + Var { + location: 25..26, + name: "b", + }, + ], + }, + pattern: Var { + location: 4..5, + name: "a", + }, + kind: Let, + annotation: None, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_unfinished_let.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_unfinished_let.snap new file mode 100644 index 00000000..2a9b3c48 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_unfinished_let.snap @@ -0,0 +1,15 @@ +--- +source: crates/aiken-lang/src/parser/expr/assignment.rs +description: "Invalid code (parse error):\n\nlet a =\n// foo\nlet b = 42\n" +--- +[ + ParseError { + kind: UnfinishedAssignmentRightHandSide, + span: 0..25, + while_parsing: None, + expected: {}, + label: Some( + "invalid assignment right-hand side", + ), + }, +] diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index 75d63e92..08853bd4 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -28,15 +28,28 @@ macro_rules! assert_expr { let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter()); - let result = $crate::parser::expr::sequence().parse(stream).unwrap(); + let result = $crate::parser::expr::sequence().parse(stream); - insta::with_settings!({ - description => concat!("Code:\n\n", indoc::indoc! { $code }), - prepend_module_to_snapshot => false, - omit_expression => true - }, { - insta::assert_debug_snapshot!(result); - }); + match result { + Ok(expr) => { + insta::with_settings!({ + description => concat!("Code:\n\n", indoc::indoc! { $code }), + prepend_module_to_snapshot => false, + omit_expression => true + }, { + insta::assert_debug_snapshot!(expr); + }) + }, + Err(err) => { + insta::with_settings!({ + description => concat!("Invalid code (parse error):\n\n", indoc::indoc! { $code }), + prepend_module_to_snapshot => false, + omit_expression => true + }, { + insta::assert_debug_snapshot!(err); + }) + } + } }; } diff --git a/examples/acceptance_tests/092/aiken.lock b/examples/acceptance_tests/092/aiken.lock new file mode 100644 index 00000000..6e350cda --- /dev/null +++ b/examples/acceptance_tests/092/aiken.lock @@ -0,0 +1,7 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +requirements = [] +packages = [] + +[etags] diff --git a/examples/acceptance_tests/092/aiken.toml b/examples/acceptance_tests/092/aiken.toml new file mode 100644 index 00000000..e1606480 --- /dev/null +++ b/examples/acceptance_tests/092/aiken.toml @@ -0,0 +1,2 @@ +name = "aiken-lang/acceptance_test_092" +version = "0.0.0" diff --git a/examples/acceptance_tests/092/lib/foo.ak b/examples/acceptance_tests/092/lib/foo.ak new file mode 100644 index 00000000..81ef9e41 --- /dev/null +++ b/examples/acceptance_tests/092/lib/foo.ak @@ -0,0 +1,41 @@ +test foo_1() { + let a = { + let b = 42 + b + } + + a == 42 +} + +test foo_2() { + expect Some(a) = { + let b = 42 + Some(b) + } + + a == 42 +} + +test foo_3() { + let c = Some(42) + + let a = { + expect Some(b) = c + b + } + + a == 42 +} + +test foo_4() { + let a = { + let b = 2 + let c = { + let d = 14 + d * b + } + c + 14 + } + + a == 42 +} diff --git a/examples/acceptance_tests/092/plutus.json b/examples/acceptance_tests/092/plutus.json new file mode 100644 index 00000000..a6a24507 --- /dev/null +++ b/examples/acceptance_tests/092/plutus.json @@ -0,0 +1,39 @@ +{ + "preamble": { + "title": "aiken-lang/acceptance_test_090", + "version": "0.0.0", + "plutusVersion": "v2", + "compiler": { + "name": "Aiken", + "version": "v1.0.21-alpha+9f263c4" + } + }, + "validators": [ + { + "title": "foo.spend", + "datum": { + "title": "datum", + "schema": { + "$ref": "#/definitions/Int" + } + }, + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "compiledCode": "583f010000322223253330053370e00290487777c9cfdde5c8f27bf4c1637fc55b5eeef7d8c4d9e0d4454967ff7d6e7ee6e242eb60c6318a4c26cac6eb400d5cd1", + "hash": "d18aa035514acb988a34d33fc246420c5b0eca4f3f947ce95e294447" + } + ], + "definitions": { + "Data": { + "title": "Data", + "description": "Any Plutus data." + }, + "Int": { + "dataType": "integer" + } + } +} \ No newline at end of file