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)] #[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,
} }
} }

View File

@ -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 {

View File

@ -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,
} }
}) })

View File

@ -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),

View File

@ -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 => "//",

View File

@ -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