Experiment with monadic bind.

This commit is contained in:
KtorZ 2024-03-10 11:26:41 +01:00
parent 0e0bed3c9d
commit 1f530f3b24
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
6 changed files with 109 additions and 20 deletions

View File

@ -1425,6 +1425,7 @@ impl Default for Bls12_381Point {
#[derive(Debug, Clone, PartialEq, Eq, Copy, serde::Serialize, serde::Deserialize)]
pub enum AssignmentKind {
Let,
Bind,
Expect,
}
@ -1440,6 +1441,7 @@ impl AssignmentKind {
pub fn location_offset(&self) -> usize {
match self {
AssignmentKind::Let => 3,
AssignmentKind::Bind => 3,
AssignmentKind::Expect => 6,
}
}

View File

@ -684,9 +684,10 @@ impl<'comments> Formatter<'comments> {
kind: AssignmentKind,
annotation: &'a Option<Annotation>,
) -> Document<'a> {
let keyword = match kind {
AssignmentKind::Let => "let",
AssignmentKind::Expect => "expect",
let (keyword, equal) = match kind {
AssignmentKind::Let => ("let", "="),
AssignmentKind::Bind => ("let", "<-"),
AssignmentKind::Expect => ("expect", "="),
};
match pattern {
@ -708,7 +709,8 @@ impl<'comments> Formatter<'comments> {
.to_doc()
.append(" ")
.append(pattern.append(annotation).group())
.append(" =")
.append(" ")
.append(equal)
.append(self.case_clause_value(value))
}
}
@ -1842,11 +1844,7 @@ impl<'a> Documentable<'a> for &'a ArgName {
}
fn pub_(public: bool) -> Document<'static> {
if public {
"pub ".to_doc()
} else {
nil()
}
if public { "pub ".to_doc() } else { nil() }
}
impl<'a> Documentable<'a> for &'a UnqualifiedImport {

View File

@ -1,10 +1,9 @@
use chumsky::prelude::*;
use crate::{
ast,
expr::UntypedExpr,
parser::{annotation, error::ParseError, pattern, token::Token},
};
use chumsky::prelude::*;
pub fn let_(
r: Recursive<'_, Token, UntypedExpr, ParseError>,
@ -12,9 +11,9 @@ pub fn let_(
just(Token::Let)
.ignore_then(pattern())
.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())
.validate(move |((pattern, annotation), value), span, emit| {
.validate(move |(((pattern, annotation), kind), value), span, emit| {
if matches!(value, UntypedExpr::Assignment { .. }) {
emit(ParseError::invalid_assignment_right_hand_side(span))
}
@ -23,7 +22,11 @@ pub fn let_(
location: span,
value: Box::new(value),
pattern,
kind: ast::AssignmentKind::Let,
kind: if kind == Token::LArrow {
ast::AssignmentKind::Bind
} else {
ast::AssignmentKind::Let
},
annotation,
}
})

View File

@ -163,6 +163,8 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
just("!=").to(Token::NotEqual),
just('!').to(Token::Bang),
just('?').to(Token::Question),
just("<-").to(Token::LArrow),
just("->").to(Token::RArrow),
choice((
just("<=").to(Token::LessEqual),
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::Plus),
just("->").to(Token::RArrow),
just('-').to(Token::Minus),
just('*').to(Token::Star),
just('/').to(Token::Slash),

View File

@ -62,6 +62,7 @@ pub enum Token {
Pipe, // '|>'
Dot, // '.'
RArrow, // '->'
LArrow, // '<-'
DotDot, // '..'
EndOfFile,
// Docs/Extra
@ -152,6 +153,7 @@ impl fmt::Display for Token {
Token::Pipe => "|>",
Token::Dot => ".",
Token::RArrow => "->",
Token::LArrow => "<-",
Token::DotDot => "..",
Token::EndOfFile => "EOF",
Token::Comment => "//",

View File

@ -24,7 +24,7 @@ use crate::{
tipo::{fields::FieldMap, PatternConstructor, TypeVar},
};
use std::{cmp::Ordering, collections::HashMap, ops::Deref, rc::Rc};
use vec1::Vec1;
use vec1::{vec1, Vec1};
#[derive(Debug)]
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
// error we emit a warning which explains that using `expect` is unnecessary.
match kind {
AssignmentKind::Bind => {
unreachable!("monadic-bind should have been desugared earlier.")
}
AssignmentKind::Let => {
self.environment
.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> {
let sequence = self.in_new_scope(|scope| {
let mut breakpoint = None;
let mut sequence = self.in_new_scope(|scope| {
let count = untyped.len();
let mut expressions = Vec::with_capacity(count);
for (i, expression) in untyped.into_iter().enumerate() {
let no_assignment = assert_no_assignment(&expression);
for (i, expression) in untyped.iter().enumerate() {
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)) {
// 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)
})?;
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
.environment
.warnings