Experiment with monadic bind.
This commit is contained in:
parent
0e0bed3c9d
commit
1f530f3b24
|
@ -1425,6 +1425,7 @@ impl Default for Bls12_381Point {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum AssignmentKind {
|
pub enum AssignmentKind {
|
||||||
Let,
|
Let,
|
||||||
|
Bind,
|
||||||
Expect,
|
Expect,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1440,6 +1441,7 @@ impl AssignmentKind {
|
||||||
pub fn location_offset(&self) -> usize {
|
pub fn location_offset(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
AssignmentKind::Let => 3,
|
AssignmentKind::Let => 3,
|
||||||
|
AssignmentKind::Bind => 3,
|
||||||
AssignmentKind::Expect => 6,
|
AssignmentKind::Expect => 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -684,9 +684,10 @@ impl<'comments> Formatter<'comments> {
|
||||||
kind: AssignmentKind,
|
kind: AssignmentKind,
|
||||||
annotation: &'a Option<Annotation>,
|
annotation: &'a Option<Annotation>,
|
||||||
) -> Document<'a> {
|
) -> Document<'a> {
|
||||||
let keyword = match kind {
|
let (keyword, equal) = match kind {
|
||||||
AssignmentKind::Let => "let",
|
AssignmentKind::Let => ("let", "="),
|
||||||
AssignmentKind::Expect => "expect",
|
AssignmentKind::Bind => ("let", "<-"),
|
||||||
|
AssignmentKind::Expect => ("expect", "="),
|
||||||
};
|
};
|
||||||
|
|
||||||
match pattern {
|
match pattern {
|
||||||
|
@ -708,7 +709,8 @@ impl<'comments> Formatter<'comments> {
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(pattern.append(annotation).group())
|
.append(pattern.append(annotation).group())
|
||||||
.append(" =")
|
.append(" ")
|
||||||
|
.append(equal)
|
||||||
.append(self.case_clause_value(value))
|
.append(self.case_clause_value(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1842,11 +1844,7 @@ impl<'a> Documentable<'a> for &'a ArgName {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pub_(public: bool) -> Document<'static> {
|
fn pub_(public: bool) -> Document<'static> {
|
||||||
if public {
|
if public { "pub ".to_doc() } else { nil() }
|
||||||
"pub ".to_doc()
|
|
||||||
} else {
|
|
||||||
nil()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Documentable<'a> for &'a UnqualifiedImport {
|
impl<'a> Documentable<'a> for &'a UnqualifiedImport {
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use chumsky::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast,
|
ast,
|
||||||
expr::UntypedExpr,
|
expr::UntypedExpr,
|
||||||
parser::{annotation, error::ParseError, pattern, token::Token},
|
parser::{annotation, error::ParseError, pattern, token::Token},
|
||||||
};
|
};
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
pub fn let_(
|
pub fn let_(
|
||||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||||
|
@ -12,9 +11,9 @@ pub fn let_(
|
||||||
just(Token::Let)
|
just(Token::Let)
|
||||||
.ignore_then(pattern())
|
.ignore_then(pattern())
|
||||||
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||||
.then_ignore(just(Token::Equal))
|
.then(choice((just(Token::Equal), just(Token::LArrow))))
|
||||||
.then(r.clone())
|
.then(r.clone())
|
||||||
.validate(move |((pattern, annotation), value), span, emit| {
|
.validate(move |(((pattern, annotation), kind), value), span, emit| {
|
||||||
if matches!(value, UntypedExpr::Assignment { .. }) {
|
if matches!(value, UntypedExpr::Assignment { .. }) {
|
||||||
emit(ParseError::invalid_assignment_right_hand_side(span))
|
emit(ParseError::invalid_assignment_right_hand_side(span))
|
||||||
}
|
}
|
||||||
|
@ -23,7 +22,11 @@ pub fn let_(
|
||||||
location: span,
|
location: span,
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
pattern,
|
pattern,
|
||||||
kind: ast::AssignmentKind::Let,
|
kind: if kind == Token::LArrow {
|
||||||
|
ast::AssignmentKind::Bind
|
||||||
|
} else {
|
||||||
|
ast::AssignmentKind::Let
|
||||||
|
},
|
||||||
annotation,
|
annotation,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -163,6 +163,8 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
just("!=").to(Token::NotEqual),
|
just("!=").to(Token::NotEqual),
|
||||||
just('!').to(Token::Bang),
|
just('!').to(Token::Bang),
|
||||||
just('?').to(Token::Question),
|
just('?').to(Token::Question),
|
||||||
|
just("<-").to(Token::LArrow),
|
||||||
|
just("->").to(Token::RArrow),
|
||||||
choice((
|
choice((
|
||||||
just("<=").to(Token::LessEqual),
|
just("<=").to(Token::LessEqual),
|
||||||
just('<').to(Token::Less),
|
just('<').to(Token::Less),
|
||||||
|
@ -170,7 +172,6 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
just('>').to(Token::Greater),
|
just('>').to(Token::Greater),
|
||||||
)),
|
)),
|
||||||
just('+').to(Token::Plus),
|
just('+').to(Token::Plus),
|
||||||
just("->").to(Token::RArrow),
|
|
||||||
just('-').to(Token::Minus),
|
just('-').to(Token::Minus),
|
||||||
just('*').to(Token::Star),
|
just('*').to(Token::Star),
|
||||||
just('/').to(Token::Slash),
|
just('/').to(Token::Slash),
|
||||||
|
|
|
@ -62,6 +62,7 @@ pub enum Token {
|
||||||
Pipe, // '|>'
|
Pipe, // '|>'
|
||||||
Dot, // '.'
|
Dot, // '.'
|
||||||
RArrow, // '->'
|
RArrow, // '->'
|
||||||
|
LArrow, // '<-'
|
||||||
DotDot, // '..'
|
DotDot, // '..'
|
||||||
EndOfFile,
|
EndOfFile,
|
||||||
// Docs/Extra
|
// Docs/Extra
|
||||||
|
@ -152,6 +153,7 @@ impl fmt::Display for Token {
|
||||||
Token::Pipe => "|>",
|
Token::Pipe => "|>",
|
||||||
Token::Dot => ".",
|
Token::Dot => ".",
|
||||||
Token::RArrow => "->",
|
Token::RArrow => "->",
|
||||||
|
Token::LArrow => "<-",
|
||||||
Token::DotDot => "..",
|
Token::DotDot => "..",
|
||||||
Token::EndOfFile => "EOF",
|
Token::EndOfFile => "EOF",
|
||||||
Token::Comment => "//",
|
Token::Comment => "//",
|
||||||
|
|
|
@ -24,7 +24,7 @@ use crate::{
|
||||||
tipo::{fields::FieldMap, PatternConstructor, TypeVar},
|
tipo::{fields::FieldMap, PatternConstructor, TypeVar},
|
||||||
};
|
};
|
||||||
use std::{cmp::Ordering, collections::HashMap, ops::Deref, rc::Rc};
|
use std::{cmp::Ordering, collections::HashMap, ops::Deref, rc::Rc};
|
||||||
use vec1::Vec1;
|
use vec1::{vec1, Vec1};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ExprTyper<'a, 'b> {
|
pub(crate) struct ExprTyper<'a, 'b> {
|
||||||
|
@ -978,6 +978,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
// If `expect` is explicitly used, we still check exhaustiveness but instead of returning an
|
// If `expect` is explicitly used, we still check exhaustiveness but instead of returning an
|
||||||
// error we emit a warning which explains that using `expect` is unnecessary.
|
// error we emit a warning which explains that using `expect` is unnecessary.
|
||||||
match kind {
|
match kind {
|
||||||
|
AssignmentKind::Bind => {
|
||||||
|
unreachable!("monadic-bind should have been desugared earlier.")
|
||||||
|
}
|
||||||
|
|
||||||
AssignmentKind::Let => {
|
AssignmentKind::Let => {
|
||||||
self.environment
|
self.environment
|
||||||
.check_exhaustiveness(&[&pattern], location, true)?
|
.check_exhaustiveness(&[&pattern], location, true)?
|
||||||
|
@ -1708,15 +1712,26 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_seq(&mut self, location: Span, untyped: Vec<UntypedExpr>) -> Result<TypedExpr, Error> {
|
fn infer_seq(&mut self, location: Span, untyped: Vec<UntypedExpr>) -> Result<TypedExpr, Error> {
|
||||||
let sequence = self.in_new_scope(|scope| {
|
let mut breakpoint = None;
|
||||||
|
|
||||||
|
let mut sequence = self.in_new_scope(|scope| {
|
||||||
let count = untyped.len();
|
let count = untyped.len();
|
||||||
|
|
||||||
let mut expressions = Vec::with_capacity(count);
|
let mut expressions = Vec::with_capacity(count);
|
||||||
|
|
||||||
for (i, expression) in untyped.into_iter().enumerate() {
|
for (i, expression) in untyped.iter().enumerate() {
|
||||||
let no_assignment = assert_no_assignment(&expression);
|
let no_assignment = assert_no_assignment(expression);
|
||||||
|
|
||||||
let typed_expression = scope.infer(expression)?;
|
let typed_expression = match expression {
|
||||||
|
UntypedExpr::Assignment {
|
||||||
|
kind: AssignmentKind::Bind,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
breakpoint = Some((i, expression.clone()));
|
||||||
|
return Ok(expressions);
|
||||||
|
}
|
||||||
|
_ => scope.infer(expression.to_owned())?,
|
||||||
|
};
|
||||||
|
|
||||||
expressions.push(match i.cmp(&(count - 1)) {
|
expressions.push(match i.cmp(&(count - 1)) {
|
||||||
// When the expression is the last in a sequence, we enforce it is NOT
|
// When the expression is the last in a sequence, we enforce it is NOT
|
||||||
|
@ -1738,6 +1753,74 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
Ok(expressions)
|
Ok(expressions)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if let Some((
|
||||||
|
i,
|
||||||
|
UntypedExpr::Assignment {
|
||||||
|
location,
|
||||||
|
value,
|
||||||
|
pattern,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
)) = breakpoint
|
||||||
|
{
|
||||||
|
let then = UntypedExpr::Sequence {
|
||||||
|
location,
|
||||||
|
expressions: untyped.into_iter().skip(i + 1).collect::<Vec<_>>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: This must be constructed based on the inferred type of *value*.
|
||||||
|
//
|
||||||
|
// let tipo = self.infer(untyped_value.clone())?.tipo();
|
||||||
|
//
|
||||||
|
// The following is the `and_then` for Option. The one for Fuzzer is a bit
|
||||||
|
// different.
|
||||||
|
let desugar = UntypedExpr::When {
|
||||||
|
location,
|
||||||
|
subject: value.clone(),
|
||||||
|
clauses: vec![
|
||||||
|
UntypedClause {
|
||||||
|
location,
|
||||||
|
guard: None,
|
||||||
|
patterns: vec1![Pattern::Constructor {
|
||||||
|
location,
|
||||||
|
is_record: false,
|
||||||
|
with_spread: false,
|
||||||
|
name: "None".to_string(),
|
||||||
|
module: None,
|
||||||
|
constructor: (),
|
||||||
|
tipo: (),
|
||||||
|
arguments: vec![],
|
||||||
|
}],
|
||||||
|
then: UntypedExpr::Var {
|
||||||
|
location,
|
||||||
|
name: "None".to_string(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UntypedClause {
|
||||||
|
location,
|
||||||
|
guard: None,
|
||||||
|
patterns: vec1![Pattern::Constructor {
|
||||||
|
location,
|
||||||
|
is_record: false,
|
||||||
|
with_spread: false,
|
||||||
|
name: "Some".to_string(),
|
||||||
|
module: None,
|
||||||
|
constructor: (),
|
||||||
|
tipo: (),
|
||||||
|
arguments: vec![CallArg {
|
||||||
|
location,
|
||||||
|
label: None,
|
||||||
|
value: pattern.clone(),
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
then,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
sequence.push(self.infer(desugar)?);
|
||||||
|
};
|
||||||
|
|
||||||
let unused = self
|
let unused = self
|
||||||
.environment
|
.environment
|
||||||
.warnings
|
.warnings
|
||||||
|
|
Loading…
Reference in New Issue