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
|
### Fixed
|
||||||
|
|
||||||
- **aiken-lang**: Fix flat encoding and decoding of large integer values. @KtorZ
|
- **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
|
- **aiken**: Ensures that test expected to fail that return `False` are considered to pass & improve error reporting when they fail. @KtorZ
|
||||||
|
|
||||||
### Removed
|
### 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
|
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 {
|
pub fn invalid_tuple_index(span: Span, index: String, suffix: Option<String>) -> Self {
|
||||||
let hint = suffix.map(|suffix| format!("Did you mean '{index}{suffix}'?"));
|
let hint = suffix.map(|suffix| format!("Did you mean '{index}{suffix}'?"));
|
||||||
Self {
|
Self {
|
||||||
|
@ -163,6 +173,16 @@ pub enum ErrorKind {
|
||||||
hint: Option<String>,
|
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))]
|
#[error("I tripped over a {}", fmt_curve_type(.curve))]
|
||||||
PointNotOnCurve { curve: CurveType },
|
PointNotOnCurve { curve: CurveType },
|
||||||
|
|
||||||
|
|
|
@ -14,15 +14,19 @@ pub fn let_(
|
||||||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||||
.then_ignore(just(Token::Equal))
|
.then_ignore(just(Token::Equal))
|
||||||
.then(r.clone())
|
.then(r.clone())
|
||||||
.map_with_span(
|
.validate(move |((pattern, annotation), value), span, emit| {
|
||||||
move |((pattern, annotation), value), span| UntypedExpr::Assignment {
|
if matches!(value, UntypedExpr::Assignment { .. }) {
|
||||||
|
emit(ParseError::invalid_assignment_right_hand_side(span))
|
||||||
|
}
|
||||||
|
|
||||||
|
UntypedExpr::Assignment {
|
||||||
location: span,
|
location: span,
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
pattern,
|
pattern,
|
||||||
kind: ast::AssignmentKind::Let,
|
kind: ast::AssignmentKind::Let,
|
||||||
annotation,
|
annotation,
|
||||||
},
|
}
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect(
|
pub fn expect(
|
||||||
|
@ -36,7 +40,7 @@ pub fn expect(
|
||||||
.or_not(),
|
.or_not(),
|
||||||
)
|
)
|
||||||
.then(r.clone())
|
.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(|| {
|
let (pattern, annotation) = opt_pattern.unwrap_or_else(|| {
|
||||||
(
|
(
|
||||||
ast::UntypedPattern::Constructor {
|
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 {
|
UntypedExpr::Assignment {
|
||||||
location: span,
|
location: span,
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
|
|
|
@ -11,7 +11,17 @@ pub fn parser(
|
||||||
choice((
|
choice((
|
||||||
sequence
|
sequence
|
||||||
.clone()
|
.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(
|
sequence.clone().delimited_by(
|
||||||
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
|
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
|
||||||
just(Token::RightParen),
|
just(Token::RightParen),
|
||||||
|
|
|
@ -1650,6 +1650,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
let mut typed_expressions = vec![];
|
let mut typed_expressions = vec![];
|
||||||
|
|
||||||
for expression in expressions {
|
for expression in expressions {
|
||||||
|
assert_no_assignment(&expression)?;
|
||||||
|
|
||||||
let typed_expression = self.infer(expression)?;
|
let typed_expression = self.infer(expression)?;
|
||||||
|
|
||||||
self.unify(
|
self.unify(
|
||||||
|
|
Loading…
Reference in New Issue