feat(backpassing): implements multi patterns
The main trick here was transforming Assignment to contain `Vec<UntypedPattern, Option<Annotation>>` in a field called patterns. This then meant that I could remove the `pattern` and `annotation` field from `Assignment`. The parser handles `=` and `<-` just fine because in the future `=` with multi patterns will mean some kind of optimization on tuples. But, since we don't have that optimization yet, when someone uses multi patterns with an `=` there will be an error returned from the type checker right where `infer_seq` looks for `backpassing`. From there the rest of the work was in `Project::backpassing` where I only needed to rework some things to work with a list of patterns instead of just one.
This commit is contained in:
parent
f02b9b0f0c
commit
b6b52ba508
|
@ -1782,6 +1782,47 @@ impl Span {
|
|||
}
|
||||
}
|
||||
|
||||
/// Map the current start and end of the Span to new values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use aiken_lang::ast::Span;
|
||||
///
|
||||
/// let span = Span { start: 0, end: 1 };
|
||||
///
|
||||
/// let other = span.map(|start, end| (start + 2, end + 4));
|
||||
///
|
||||
/// assert_eq!(other.start, 2);
|
||||
/// assert_eq!(other.end, 5);
|
||||
/// ```
|
||||
pub fn map<F: FnOnce(usize, usize) -> (usize, usize)>(&self, f: F) -> Self {
|
||||
let (start, end) = f(self.start, self.end);
|
||||
|
||||
Self { start, end }
|
||||
}
|
||||
|
||||
/// Map the current end of the Span to a new value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use aiken_lang::ast::Span;
|
||||
///
|
||||
/// let span = Span { start: 0, end: 1 };
|
||||
///
|
||||
/// let other = span.map_end(|end| end + 1);
|
||||
///
|
||||
/// assert_eq!(other.start, 0);
|
||||
/// assert_eq!(other.end, 2);
|
||||
/// ```
|
||||
pub fn map_end<F: FnOnce(usize) -> usize>(&self, f: F) -> Self {
|
||||
Self {
|
||||
start: self.start,
|
||||
end: f(self.end),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, byte_index: usize) -> bool {
|
||||
byte_index >= self.start && byte_index < self.end
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
Curve, DataType, DataTypeKey, DefinitionLocation, IfBranch, Located, LogicalOpChainKind,
|
||||
ParsedCallArg, Pattern, RecordConstructorArg, RecordUpdateSpread, Span, TraceKind,
|
||||
TypedAssignmentKind, TypedClause, TypedDataType, TypedRecordUpdateArg, UnOp,
|
||||
UntypedAssignmentKind, UntypedClause, UntypedRecordUpdateArg,
|
||||
UntypedAssignmentKind, UntypedClause, UntypedPattern, UntypedRecordUpdateArg,
|
||||
},
|
||||
builtins::void,
|
||||
parser::token::Base,
|
||||
|
@ -518,9 +518,8 @@ pub enum UntypedExpr {
|
|||
Assignment {
|
||||
location: Span,
|
||||
value: Box<Self>,
|
||||
pattern: Pattern<(), ()>,
|
||||
patterns: Vec1<(UntypedPattern, Option<Annotation>)>,
|
||||
kind: UntypedAssignmentKind,
|
||||
annotation: Option<Annotation>,
|
||||
},
|
||||
|
||||
Trace {
|
||||
|
@ -1300,11 +1299,13 @@ impl UntypedExpr {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn lambda(name: String, expressions: Vec<UntypedExpr>, location: Span) -> Self {
|
||||
pub fn lambda(names: Vec<String>, expressions: Vec<UntypedExpr>, location: Span) -> Self {
|
||||
Self::Fn {
|
||||
location,
|
||||
fn_style: FnStyle::Plain,
|
||||
arguments: vec![Arg {
|
||||
arguments: names
|
||||
.into_iter()
|
||||
.map(|name| Arg {
|
||||
location,
|
||||
doc: None,
|
||||
annotation: None,
|
||||
|
@ -1315,7 +1316,8 @@ impl UntypedExpr {
|
|||
location,
|
||||
is_validator_param: false,
|
||||
},
|
||||
}],
|
||||
})
|
||||
.collect(),
|
||||
body: Self::Sequence {
|
||||
location,
|
||||
expressions,
|
||||
|
|
|
@ -679,10 +679,9 @@ impl<'comments> Formatter<'comments> {
|
|||
|
||||
fn assignment<'a>(
|
||||
&mut self,
|
||||
pattern: &'a UntypedPattern,
|
||||
patterns: &'a Vec1<(UntypedPattern, Option<Annotation>)>,
|
||||
value: &'a UntypedExpr,
|
||||
kind: UntypedAssignmentKind,
|
||||
annotation: &'a Option<Annotation>,
|
||||
) -> Document<'a> {
|
||||
let keyword = match kind {
|
||||
AssignmentKind::Let { .. } => "let",
|
||||
|
@ -691,13 +690,21 @@ impl<'comments> Formatter<'comments> {
|
|||
|
||||
let symbol = if kind.is_backpassing() { "<-" } else { "=" };
|
||||
|
||||
match pattern {
|
||||
match patterns.first() {
|
||||
(
|
||||
UntypedPattern::Constructor {
|
||||
name, module: None, ..
|
||||
} if name == "True" && annotation.is_none() && kind.is_expect() => {
|
||||
},
|
||||
annotation,
|
||||
) if name == "True"
|
||||
&& annotation.is_none()
|
||||
&& kind.is_expect()
|
||||
&& patterns.len() == 1 =>
|
||||
{
|
||||
keyword.to_doc().append(self.case_clause_value(value))
|
||||
}
|
||||
_ => {
|
||||
let patterns = patterns.into_iter().map(|(pattern, annotation)| {
|
||||
self.pop_empty_lines(pattern.location().end);
|
||||
|
||||
let pattern = self.pattern(pattern);
|
||||
|
@ -706,11 +713,16 @@ impl<'comments> Formatter<'comments> {
|
|||
.as_ref()
|
||||
.map(|a| ": ".to_doc().append(self.annotation(a)));
|
||||
|
||||
pattern.append(annotation).group()
|
||||
});
|
||||
|
||||
keyword
|
||||
.to_doc()
|
||||
.append(" ")
|
||||
.append(pattern.append(annotation).group())
|
||||
.append(" ")
|
||||
.append(break_("", " "))
|
||||
.append(join(patterns, break_(",", ", ")))
|
||||
.nest(INDENT)
|
||||
.append(break_(",", ""))
|
||||
.append(break_("", " "))
|
||||
.append(symbol)
|
||||
.append(self.case_clause_value(value))
|
||||
}
|
||||
|
@ -907,11 +919,10 @@ impl<'comments> Formatter<'comments> {
|
|||
|
||||
UntypedExpr::Assignment {
|
||||
value,
|
||||
pattern,
|
||||
annotation,
|
||||
patterns,
|
||||
kind,
|
||||
..
|
||||
} => self.assignment(pattern, value, *kind, annotation),
|
||||
} => self.assignment(patterns, value, *kind),
|
||||
|
||||
UntypedExpr::Trace {
|
||||
kind, text, then, ..
|
||||
|
|
|
@ -14,7 +14,9 @@ Test(
|
|||
location: 45..50,
|
||||
name: "False",
|
||||
},
|
||||
pattern: Constructor {
|
||||
patterns: [
|
||||
(
|
||||
Constructor {
|
||||
is_record: false,
|
||||
location: 38..42,
|
||||
name: "True",
|
||||
|
@ -24,10 +26,12 @@ Test(
|
|||
with_spread: false,
|
||||
tipo: (),
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Expect {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Var {
|
||||
location: 54..59,
|
||||
|
|
|
@ -25,14 +25,18 @@ Fn(
|
|||
},
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 17..18,
|
||||
name: "x",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
doc: None,
|
||||
location: 0..8,
|
||||
|
|
|
@ -9,23 +9,30 @@ pub fn let_(
|
|||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
just(Token::Let)
|
||||
.ignore_then(pattern())
|
||||
.ignore_then(
|
||||
pattern()
|
||||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||
.separated_by(just(Token::Comma))
|
||||
.at_least(1),
|
||||
)
|
||||
.then(choice((just(Token::Equal), just(Token::LArrow))))
|
||||
.then(r.clone())
|
||||
.validate(move |(((pattern, annotation), kind), value), span, emit| {
|
||||
.validate(move |((patterns, kind), value), span, emit| {
|
||||
if matches!(value, UntypedExpr::Assignment { .. }) {
|
||||
emit(ParseError::invalid_assignment_right_hand_side(span))
|
||||
}
|
||||
|
||||
let patterns = patterns
|
||||
.try_into()
|
||||
.expect("We use at_least(1) so this should never be empty");
|
||||
|
||||
UntypedExpr::Assignment {
|
||||
location: span,
|
||||
value: Box::new(value),
|
||||
pattern,
|
||||
patterns,
|
||||
kind: ast::AssignmentKind::Let {
|
||||
backpassing: kind == Token::LArrow,
|
||||
},
|
||||
annotation,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -37,14 +44,20 @@ pub fn expect(
|
|||
.ignore_then(
|
||||
pattern()
|
||||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||
.separated_by(just(Token::Comma))
|
||||
.at_least(1)
|
||||
.then(choice((just(Token::Equal), just(Token::LArrow))))
|
||||
.or_not(),
|
||||
)
|
||||
.then(r.clone())
|
||||
.validate(move |(opt_pattern, value), span, emit| {
|
||||
let ((pattern, annotation), kind) = opt_pattern.unwrap_or_else(|| {
|
||||
(
|
||||
if matches!(value, UntypedExpr::Assignment { .. }) {
|
||||
emit(ParseError::invalid_assignment_right_hand_side(span))
|
||||
}
|
||||
|
||||
let (patterns, kind) = opt_pattern.unwrap_or_else(|| {
|
||||
(
|
||||
vec![(
|
||||
ast::UntypedPattern::Constructor {
|
||||
is_record: false,
|
||||
location: span,
|
||||
|
@ -56,23 +69,22 @@ pub fn expect(
|
|||
tipo: (),
|
||||
},
|
||||
None,
|
||||
),
|
||||
)],
|
||||
Token::Equal,
|
||||
)
|
||||
});
|
||||
|
||||
if matches!(value, UntypedExpr::Assignment { .. }) {
|
||||
emit(ParseError::invalid_assignment_right_hand_side(span))
|
||||
}
|
||||
let patterns = patterns
|
||||
.try_into()
|
||||
.expect("We use at_least(1) so this should never be empty");
|
||||
|
||||
UntypedExpr::Assignment {
|
||||
location: span,
|
||||
patterns,
|
||||
value: Box::new(value),
|
||||
pattern,
|
||||
kind: ast::AssignmentKind::Expect {
|
||||
backpassing: kind == Token::LArrow,
|
||||
},
|
||||
annotation,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -16,14 +16,18 @@ Assignment {
|
|||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 16..17,
|
||||
name: "x",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
BinOp {
|
||||
location: 24..29,
|
||||
|
@ -42,12 +46,16 @@ Assignment {
|
|||
},
|
||||
],
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 4..5,
|
||||
name: "b",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -12,7 +12,9 @@ Assignment {
|
|||
name: "something",
|
||||
},
|
||||
},
|
||||
pattern: Constructor {
|
||||
patterns: [
|
||||
(
|
||||
Constructor {
|
||||
is_record: false,
|
||||
location: 7..14,
|
||||
name: "Some",
|
||||
|
@ -31,8 +33,10 @@ Assignment {
|
|||
with_spread: false,
|
||||
tipo: (),
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Expect {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@ Assignment {
|
|||
name: "wow",
|
||||
},
|
||||
},
|
||||
pattern: Constructor {
|
||||
patterns: [
|
||||
(
|
||||
Constructor {
|
||||
is_record: false,
|
||||
location: 0..29,
|
||||
name: "True",
|
||||
|
@ -30,8 +32,10 @@ Assignment {
|
|||
with_spread: false,
|
||||
tipo: (),
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Expect {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -16,18 +16,24 @@ Assignment {
|
|||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 13..14,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
pattern: Constructor {
|
||||
patterns: [
|
||||
(
|
||||
Constructor {
|
||||
is_record: false,
|
||||
location: 0..21,
|
||||
name: "True",
|
||||
|
@ -37,8 +43,10 @@ Assignment {
|
|||
with_spread: false,
|
||||
tipo: (),
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Expect {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -16,23 +16,31 @@ Assignment {
|
|||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 14..15,
|
||||
name: "b",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 4..5,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -16,23 +16,31 @@ Assignment {
|
|||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 14..15,
|
||||
name: "b",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 4..5,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -16,14 +16,18 @@ Assignment {
|
|||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 16..17,
|
||||
name: "b",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Var {
|
||||
location: 25..26,
|
||||
|
@ -31,12 +35,16 @@ Assignment {
|
|||
},
|
||||
],
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 4..5,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ Assignment {
|
|||
name: "foo",
|
||||
},
|
||||
},
|
||||
pattern: Constructor {
|
||||
patterns: [
|
||||
(
|
||||
Constructor {
|
||||
is_record: false,
|
||||
location: 0..11,
|
||||
name: "True",
|
||||
|
@ -21,8 +23,10 @@ Assignment {
|
|||
with_spread: false,
|
||||
tipo: (),
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Expect {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -27,14 +27,18 @@ Sequence {
|
|||
},
|
||||
location: 8..18,
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 4..5,
|
||||
name: "x",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Assignment {
|
||||
location: 20..65,
|
||||
|
@ -113,14 +117,18 @@ Sequence {
|
|||
},
|
||||
return_annotation: None,
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 24..33,
|
||||
name: "map_add_x",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Call {
|
||||
arguments: [
|
||||
|
|
|
@ -14,14 +14,18 @@ Sequence {
|
|||
numeric_underscore: true,
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 8..9,
|
||||
name: "i",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Assignment {
|
||||
location: 24..41,
|
||||
|
@ -32,14 +36,18 @@ Sequence {
|
|||
numeric_underscore: true,
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 28..29,
|
||||
name: "j",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Assignment {
|
||||
location: 44..59,
|
||||
|
@ -54,14 +62,18 @@ Sequence {
|
|||
},
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 48..49,
|
||||
name: "k",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
@ -28,12 +28,16 @@ Assignment {
|
|||
],
|
||||
tail: None,
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 4..9,
|
||||
name: "thing",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
}
|
||||
|
|
|
@ -40,14 +40,18 @@ Sequence {
|
|||
},
|
||||
],
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 4..9,
|
||||
name: "tuple",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
BinOp {
|
||||
location: 25..70,
|
||||
|
|
|
@ -27,14 +27,18 @@ Sequence {
|
|||
},
|
||||
location: 8..15,
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 4..5,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Tuple {
|
||||
location: 16..23,
|
||||
|
|
|
@ -85,14 +85,18 @@ When {
|
|||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 55..62,
|
||||
name: "amazing",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Var {
|
||||
location: 71..78,
|
||||
|
|
|
@ -19,14 +19,18 @@ Module {
|
|||
location: 23..26,
|
||||
name: "bar",
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 19..20,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
UInt {
|
||||
location: 30..32,
|
||||
|
@ -59,14 +63,18 @@ Module {
|
|||
location: 60..63,
|
||||
name: "bar",
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 56..57,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
UInt {
|
||||
location: 67..69,
|
||||
|
@ -110,14 +118,18 @@ Module {
|
|||
},
|
||||
},
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 93..94,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
doc: None,
|
||||
location: 74..84,
|
||||
|
@ -157,14 +169,18 @@ Module {
|
|||
},
|
||||
location: 130..137,
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 126..127,
|
||||
name: "a",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
BinOp {
|
||||
location: 141..153,
|
||||
|
|
|
@ -24,14 +24,18 @@ Module {
|
|||
],
|
||||
preferred_format: Utf8String,
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 17..18,
|
||||
name: "x",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Var {
|
||||
location: 29..30,
|
||||
|
|
|
@ -22,14 +22,18 @@ Module {
|
|||
],
|
||||
preferred_format: Utf8String,
|
||||
},
|
||||
pattern: Var {
|
||||
patterns: [
|
||||
(
|
||||
Var {
|
||||
location: 17..18,
|
||||
name: "x",
|
||||
},
|
||||
None,
|
||||
),
|
||||
],
|
||||
kind: Let {
|
||||
backpassing: false,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
Var {
|
||||
location: 27..28,
|
||||
|
|
|
@ -1383,6 +1383,74 @@ fn backpassing_expect_type_mismatch() {
|
|||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backpassing_multi_args() {
|
||||
let source_code = r#"
|
||||
fn fold(list: List<a>, init: b, then: fn(a, b) -> b) -> b {
|
||||
when list is {
|
||||
[] -> init
|
||||
[x, ..rest] -> fold(rest, then(x, init), then)
|
||||
}
|
||||
}
|
||||
|
||||
fn backpassing() -> Int {
|
||||
let elem, acc <- fold([1, 2, 3], 0)
|
||||
|
||||
elem + acc
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(check(parse(source_code)).is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backpassing_multi_args_expect() {
|
||||
let source_code = r#"
|
||||
pub type Bar {
|
||||
Foo(Int)
|
||||
Wow(Int)
|
||||
}
|
||||
|
||||
fn fold(list: List<a>, init: b, then: fn(a, b) -> b) -> b {
|
||||
when list is {
|
||||
[] -> init
|
||||
[x, ..rest] -> fold(rest, then(x, init), then)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backpassing() -> Bar {
|
||||
expect Foo(elem), Wow(acc) <- fold([Foo(1), Foo(2), Foo(3)], Wow(0))
|
||||
|
||||
Wow(elem + acc)
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(matches!(check(parse(source_code)), Ok((warnings, _)) if warnings.is_empty()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backpassing_multi_args_using_equals() {
|
||||
let source_code = r#"
|
||||
fn fold(list: List<a>, init: b, then: fn(a, b) -> b) -> b {
|
||||
when list is {
|
||||
[] -> init
|
||||
[x, ..rest] -> fold(rest, then(x, init), then)
|
||||
}
|
||||
}
|
||||
|
||||
fn backpassing() -> Int {
|
||||
let elem, acc = fold([1, 2, 3], 0, fn(elem, acc) { elem + acc })
|
||||
|
||||
elem + acc
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(matches!(
|
||||
check(parse(source_code)),
|
||||
Err((_, Error::UnexpectedMultiPatternAssignment { .. }))
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trace_if_false_ko() {
|
||||
let source_code = r#"
|
||||
|
|
|
@ -707,6 +707,19 @@ Perhaps, try the following:
|
|||
with_spread: bool,
|
||||
},
|
||||
|
||||
#[error("I discovered a regular let assignment with multiple patterns.\n")]
|
||||
#[diagnostic(code("unexpected::multi_pattern_assignment"))]
|
||||
#[diagnostic(help(
|
||||
"Did you mean to use backpassing syntax with {}?",
|
||||
"<-".if_supports_color(Stdout, |s| s.purple())
|
||||
))]
|
||||
UnexpectedMultiPatternAssignment {
|
||||
#[label("unexpected")]
|
||||
location: Span,
|
||||
#[label("<-")]
|
||||
arrow: Span,
|
||||
},
|
||||
|
||||
#[error("I tripped over some unknown labels in a pattern or function.\n")]
|
||||
#[diagnostic(code("unknown::labels"))]
|
||||
UnknownLabels(#[related] Vec<UnknownLabels>),
|
||||
|
@ -1031,6 +1044,7 @@ impl ExtraData for Error {
|
|||
| Error::ValidatorImported { .. }
|
||||
| Error::IncorrectTestArity { .. }
|
||||
| Error::GenericLeftAtBoundary { .. }
|
||||
| Error::UnexpectedMultiPatternAssignment { .. }
|
||||
| Error::ValidatorMustReturnBool { .. } => None,
|
||||
|
||||
Error::UnknownType { name, .. }
|
||||
|
|
|
@ -262,12 +262,17 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
|
||||
UntypedExpr::Assignment {
|
||||
location,
|
||||
pattern,
|
||||
patterns,
|
||||
value,
|
||||
kind,
|
||||
annotation,
|
||||
..
|
||||
} => self.infer_assignment(pattern, *value, kind, &annotation, location),
|
||||
} => {
|
||||
// at this point due to backpassing rewrites,
|
||||
// patterns is guaranteed to have one item
|
||||
let (pattern, annotation) = patterns.into_vec().swap_remove(0);
|
||||
|
||||
self.infer_assignment(pattern, *value, kind, &annotation, location)
|
||||
}
|
||||
|
||||
UntypedExpr::Trace {
|
||||
location,
|
||||
|
@ -340,7 +345,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
location,
|
||||
value,
|
||||
op,
|
||||
} => self.infer_un_op(location, value, op),
|
||||
} => self.infer_un_op(location, *value, op),
|
||||
|
||||
UntypedExpr::TraceIfFalse { value, location } => {
|
||||
self.infer_trace_if_false(*value, location)
|
||||
|
@ -663,10 +668,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
fn infer_un_op(
|
||||
&mut self,
|
||||
location: Span,
|
||||
value: Box<UntypedExpr>,
|
||||
value: UntypedExpr,
|
||||
op: UnOp,
|
||||
) -> Result<TypedExpr, Error> {
|
||||
let value = self.infer(*value)?;
|
||||
let value = self.infer(value)?;
|
||||
|
||||
let tipo = match op {
|
||||
UnOp::Not => bool(),
|
||||
|
@ -949,19 +954,20 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
)?
|
||||
} else {
|
||||
if value_is_data && !untyped_pattern.is_var() && !untyped_pattern.is_discard() {
|
||||
let ann = Annotation::Constructor {
|
||||
location: Span::empty(),
|
||||
module: None,
|
||||
name: "Type".to_string(),
|
||||
arguments: vec![],
|
||||
};
|
||||
|
||||
return Err(Error::CastDataNoAnn {
|
||||
location,
|
||||
value: UntypedExpr::Assignment {
|
||||
location,
|
||||
value: untyped_value.into(),
|
||||
pattern: untyped_pattern,
|
||||
patterns: Vec1::new((untyped_pattern, Some(ann))),
|
||||
kind,
|
||||
annotation: Some(Annotation::Constructor {
|
||||
location: Span::empty(),
|
||||
module: None,
|
||||
name: "Type".to_string(),
|
||||
arguments: vec![],
|
||||
}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -1007,17 +1013,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
name: "...".to_string(),
|
||||
location: Span::empty(),
|
||||
}),
|
||||
pattern: untyped_pattern,
|
||||
patterns: Vec1::new((untyped_pattern, None)),
|
||||
kind: AssignmentKind::Let { backpassing: true },
|
||||
annotation: None,
|
||||
}
|
||||
}
|
||||
_ => UntypedExpr::Assignment {
|
||||
location: Span::empty(),
|
||||
value: Box::new(untyped_value),
|
||||
pattern: untyped_pattern,
|
||||
patterns: Vec1::new((untyped_pattern, None)),
|
||||
kind: AssignmentKind::let_(),
|
||||
annotation: None,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -1721,17 +1725,20 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
PipeTyper::infer(self, expressions)
|
||||
}
|
||||
|
||||
fn backpass(&mut self, breakpoint: UntypedExpr, continuation: Vec<UntypedExpr>) -> UntypedExpr {
|
||||
let (value, pattern, annotation, kind, assign_location) = match breakpoint {
|
||||
UntypedExpr::Assignment {
|
||||
location,
|
||||
fn backpass(
|
||||
&mut self,
|
||||
breakpoint: UntypedExpr,
|
||||
mut continuation: Vec<UntypedExpr>,
|
||||
) -> UntypedExpr {
|
||||
let UntypedExpr::Assignment {
|
||||
location: assign_location,
|
||||
value,
|
||||
pattern,
|
||||
annotation,
|
||||
kind,
|
||||
patterns,
|
||||
..
|
||||
} => (value, pattern, annotation, kind, location),
|
||||
_ => unreachable!("backpass misuse: breakpoint isn't an Assignment ?!"),
|
||||
} = breakpoint
|
||||
else {
|
||||
unreachable!("backpass misuse: breakpoint isn't an Assignment ?!");
|
||||
};
|
||||
|
||||
let value_location = value.location();
|
||||
|
@ -1744,33 +1751,41 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
.unwrap_or_else(|| value_location.end),
|
||||
};
|
||||
|
||||
let mut names = Vec::new();
|
||||
|
||||
for (index, (pattern, annotation)) in patterns.into_iter().enumerate() {
|
||||
// In case where we have a Pattern that isn't simply a let-binding to a name, we do insert an extra let-binding
|
||||
// in front of the continuation sequence. This is because we do not support patterns in function argument
|
||||
// (which is perhaps something we should support?).
|
||||
let (name, continuation) = match pattern {
|
||||
match pattern {
|
||||
Pattern::Var { name, .. } | Pattern::Discard { name, .. } if kind.is_let() => {
|
||||
(name.clone(), continuation)
|
||||
names.push(name.clone());
|
||||
}
|
||||
_ => {
|
||||
let mut with_assignment = vec![UntypedExpr::Assignment {
|
||||
let name = format!("{}_{}", ast::BACKPASS_VARIABLE, index);
|
||||
|
||||
continuation.insert(
|
||||
0,
|
||||
UntypedExpr::Assignment {
|
||||
location: assign_location,
|
||||
value: UntypedExpr::Var {
|
||||
location: value_location,
|
||||
name: ast::BACKPASS_VARIABLE.to_string(),
|
||||
name: name.clone(),
|
||||
}
|
||||
.into(),
|
||||
pattern,
|
||||
// Erase backpassing while preserving assignment kind.
|
||||
patterns: Vec1::new((pattern, annotation)),
|
||||
// erase backpassing while preserving assignment kind.
|
||||
kind: match kind {
|
||||
AssignmentKind::Let { .. } => AssignmentKind::let_(),
|
||||
AssignmentKind::Expect { .. } => AssignmentKind::expect(),
|
||||
},
|
||||
annotation,
|
||||
}];
|
||||
with_assignment.extend(continuation);
|
||||
(ast::BACKPASS_VARIABLE.to_string(), with_assignment)
|
||||
},
|
||||
);
|
||||
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match *value {
|
||||
UntypedExpr::Call { fun, arguments, .. } => {
|
||||
|
@ -1779,7 +1794,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
new_arguments.push(CallArg {
|
||||
location: call_location,
|
||||
label: None,
|
||||
value: UntypedExpr::lambda(name, continuation, call_location),
|
||||
value: UntypedExpr::lambda(names, continuation, call_location),
|
||||
});
|
||||
|
||||
UntypedExpr::Call {
|
||||
|
@ -1811,7 +1826,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
arguments: vec![CallArg {
|
||||
location: call_location,
|
||||
label: None,
|
||||
value: UntypedExpr::lambda(name, continuation, call_location),
|
||||
value: UntypedExpr::lambda(names, continuation, call_location),
|
||||
}],
|
||||
};
|
||||
|
||||
|
@ -1838,7 +1853,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
arguments: vec![CallArg {
|
||||
location: call_location,
|
||||
label: None,
|
||||
value: UntypedExpr::lambda(name, continuation, call_location),
|
||||
value: UntypedExpr::lambda(names, continuation, call_location),
|
||||
}],
|
||||
},
|
||||
}
|
||||
|
@ -1857,10 +1872,28 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
UntypedExpr::Assignment { kind, .. } if kind.is_backpassing() => {
|
||||
breakpoint = Some(expression);
|
||||
}
|
||||
UntypedExpr::Assignment {
|
||||
patterns, location, ..
|
||||
} if patterns.len() > 1 => {
|
||||
return Err(Error::UnexpectedMultiPatternAssignment {
|
||||
arrow: patterns
|
||||
.last()
|
||||
.0
|
||||
.location()
|
||||
.map(|_, c_end| (c_end + 1, c_end + 1)),
|
||||
location: patterns[1..]
|
||||
.iter()
|
||||
.map(|(p, _)| p.location())
|
||||
.reduce(|acc, loc| acc.union(loc))
|
||||
.unwrap_or(location)
|
||||
.map_end(|current| current - 1),
|
||||
});
|
||||
}
|
||||
_ => prefix.push(expression),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(breakpoint) = breakpoint {
|
||||
prefix.push(self.backpass(breakpoint, suffix));
|
||||
return self.infer_seq(location, prefix);
|
||||
|
@ -2149,9 +2182,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
sample: UntypedExpr::Assignment {
|
||||
location: Span::empty(),
|
||||
value: Box::new(subject.clone()),
|
||||
pattern: clauses[0].patterns[0].clone(),
|
||||
patterns: Vec1::new((clauses[0].patterns[0].clone(), None)),
|
||||
kind: AssignmentKind::let_(),
|
||||
annotation: None,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue