Merge pull request #807 from aiken-lang/rvcas/various-fixes
Various Fixes for parsing and checking
This commit is contained in:
commit
9ee2d58ba3
|
@ -10,6 +10,10 @@
|
|||
### Fixed
|
||||
|
||||
- **aiken-lang**: Fix flat encoding and decoding of large integer values. @KtorZ
|
||||
- **aiken-lang**: Traces should not have following expressions formatted into a block. @rvcas
|
||||
- **aiken-lang**: Sequences should not be erased if the sole expression is an assignment. @rvcas
|
||||
- **aiken-lang**: Should not be able to assign an assignment to an assignment. @rvcas
|
||||
- **aiken-lang**: Should not be able to have an assignment in a logical op chain. @rvcas
|
||||
- **aiken**: Ensures that test expected to fail that return `False` are considered to pass & improve error reporting when they fail. @KtorZ
|
||||
|
||||
### Removed
|
||||
|
|
|
@ -109,4 +109,15 @@ mod tests {
|
|||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_assignment_only() {
|
||||
assert_definition!(
|
||||
r#"
|
||||
fn run() {
|
||||
let x = 1 + 1
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/definition/function.rs
|
||||
description: "Code:\n\nfn run() {\n let x = 1 + 1\n}\n"
|
||||
---
|
||||
Fn(
|
||||
Function {
|
||||
arguments: [],
|
||||
body: Assignment {
|
||||
location: 13..26,
|
||||
value: BinOp {
|
||||
location: 21..26,
|
||||
name: AddInt,
|
||||
left: UInt {
|
||||
location: 21..22,
|
||||
value: "1",
|
||||
base: Decimal {
|
||||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
right: UInt {
|
||||
location: 25..26,
|
||||
value: "1",
|
||||
base: Decimal {
|
||||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
location: 17..18,
|
||||
name: "x",
|
||||
},
|
||||
kind: Let,
|
||||
annotation: None,
|
||||
},
|
||||
doc: None,
|
||||
location: 0..8,
|
||||
name: "run",
|
||||
public: false,
|
||||
return_annotation: None,
|
||||
return_type: (),
|
||||
end_position: 27,
|
||||
can_error: true,
|
||||
},
|
||||
)
|
|
@ -28,6 +28,16 @@ impl ParseError {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn invalid_assignment_right_hand_side(span: Span) -> Self {
|
||||
Self {
|
||||
kind: ErrorKind::UnfinishedAssignmentRightHandSide,
|
||||
span,
|
||||
while_parsing: None,
|
||||
expected: HashSet::new(),
|
||||
label: Some("invalid assignment right-hand side"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalid_tuple_index(span: Span, index: String, suffix: Option<String>) -> Self {
|
||||
let hint = suffix.map(|suffix| format!("Did you mean '{index}{suffix}'?"));
|
||||
Self {
|
||||
|
@ -163,6 +173,16 @@ pub enum ErrorKind {
|
|||
hint: Option<String>,
|
||||
},
|
||||
|
||||
#[error("I spotted an unfinished assignment.")]
|
||||
#[diagnostic(
|
||||
help(
|
||||
"{} and {} bindings must be followed by a valid, complete, expression.",
|
||||
"let".if_supports_color(Stdout, |s| s.yellow()),
|
||||
"expect".if_supports_color(Stdout, |s| s.yellow()),
|
||||
),
|
||||
)]
|
||||
UnfinishedAssignmentRightHandSide,
|
||||
|
||||
#[error("I tripped over a {}", fmt_curve_type(.curve))]
|
||||
PointNotOnCurve { curve: CurveType },
|
||||
|
||||
|
|
|
@ -14,15 +14,19 @@ pub fn let_(
|
|||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||
.then_ignore(just(Token::Equal))
|
||||
.then(r.clone())
|
||||
.map_with_span(
|
||||
move |((pattern, annotation), value), span| UntypedExpr::Assignment {
|
||||
.validate(move |((pattern, annotation), value), span, emit| {
|
||||
if matches!(value, UntypedExpr::Assignment { .. }) {
|
||||
emit(ParseError::invalid_assignment_right_hand_side(span))
|
||||
}
|
||||
|
||||
UntypedExpr::Assignment {
|
||||
location: span,
|
||||
value: Box::new(value),
|
||||
pattern,
|
||||
kind: ast::AssignmentKind::Let,
|
||||
annotation,
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn expect(
|
||||
|
@ -36,7 +40,7 @@ pub fn expect(
|
|||
.or_not(),
|
||||
)
|
||||
.then(r.clone())
|
||||
.map_with_span(move |(opt_pattern, value), span| {
|
||||
.validate(move |(opt_pattern, value), span, emit| {
|
||||
let (pattern, annotation) = opt_pattern.unwrap_or_else(|| {
|
||||
(
|
||||
ast::UntypedPattern::Constructor {
|
||||
|
@ -53,6 +57,10 @@ pub fn expect(
|
|||
)
|
||||
});
|
||||
|
||||
if matches!(value, UntypedExpr::Assignment { .. }) {
|
||||
emit(ParseError::invalid_assignment_right_hand_side(span))
|
||||
}
|
||||
|
||||
UntypedExpr::Assignment {
|
||||
location: span,
|
||||
value: Box::new(value),
|
||||
|
@ -86,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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,16 @@ pub fn parser(
|
|||
just(Token::RightParen),
|
||||
),
|
||||
))
|
||||
.map_with_span(|e, span| {
|
||||
if matches!(e, UntypedExpr::Assignment { .. }) {
|
||||
UntypedExpr::Sequence {
|
||||
location: span,
|
||||
expressions: vec![e],
|
||||
}
|
||||
} else {
|
||||
e
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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",
|
||||
),
|
||||
},
|
||||
]
|
|
@ -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);
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -407,10 +407,10 @@ Perhaps, try the following:
|
|||
as, expect, check, const, else, fn, if, is, let, opaque, pub, test, todo, trace, type, use, when"#))]
|
||||
KeywordInModuleName { name: String, keyword: String },
|
||||
|
||||
#[error("I discovered a function which is ending with an assignment.\n")]
|
||||
#[error("I discovered a block which is ending with an assignment.\n")]
|
||||
#[diagnostic(url("https://aiken-lang.org/language-tour/functions#named-functions"))]
|
||||
#[diagnostic(code("illegal::return"))]
|
||||
#[diagnostic(help(r#"In Aiken, functions must return an explicit result in the form of an expression. While assignments are technically speaking expressions, they aren't allowed to be the last expression of a function because they convey a different meaning and this could be error-prone.
|
||||
#[diagnostic(help(r#"In Aiken, code blocks (such as function bodies) must return an explicit result in the form of an expression. While assignments are technically speaking expressions, they aren't allowed to be the last expression of a function because they convey a different meaning and this could be error-prone.
|
||||
|
||||
If you really meant to return that last expression, try to replace it with the following:
|
||||
|
||||
|
|
|
@ -1650,6 +1650,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
let mut typed_expressions = vec![];
|
||||
|
||||
for expression in expressions {
|
||||
assert_no_assignment(&expression)?;
|
||||
|
||||
let typed_expression = self.infer(expression)?;
|
||||
|
||||
self.unify(
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
# This file was generated by Aiken
|
||||
# You typically do not need to edit this file
|
||||
|
||||
requirements = []
|
||||
packages = []
|
||||
|
||||
[etags]
|
|
@ -0,0 +1,2 @@
|
|||
name = "aiken-lang/acceptance_test_092"
|
||||
version = "0.0.0"
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue