feat: parser and check fixes
- do not erase sequences if the sole expression is an assignment - emit parse error if an assignment is assigned to an assignment - do not allow assignments in logical op chains
This commit is contained in:
parent
8a90e9eda0
commit
25a837ab3f
|
@ -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::InvalidAssignmentRightHandSide,
|
||||
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 discovered an invalid assignment.")]
|
||||
#[diagnostic(
|
||||
help(
|
||||
"{} and {} bindings must be followed by a valid expression.",
|
||||
"let".if_supports_color(Stdout, |s| s.yellow()),
|
||||
"expect".if_supports_color(Stdout, |s| s.yellow()),
|
||||
),
|
||||
)]
|
||||
InvalidAssignmentRightHandSide,
|
||||
|
||||
#[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),
|
||||
|
|
|
@ -11,7 +11,17 @@ pub fn parser(
|
|||
choice((
|
||||
sequence
|
||||
.clone()
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
|
||||
.map_with_span(|e, span| {
|
||||
if matches!(e, UntypedExpr::Assignment { .. }) {
|
||||
UntypedExpr::Sequence {
|
||||
location: span,
|
||||
expressions: vec![e],
|
||||
}
|
||||
} else {
|
||||
e
|
||||
}
|
||||
}),
|
||||
sequence.clone().delimited_by(
|
||||
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
|
||||
just(Token::RightParen),
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue