feat(tipo): inference for and/or chains

This commit is contained in:
rvcas 2023-08-14 17:19:24 -04:00 committed by Lucas
parent ab3a418b9c
commit e4ef386c44
4 changed files with 82 additions and 9 deletions

View File

@ -6,7 +6,11 @@ use crate::{
}; };
use miette::Diagnostic; use miette::Diagnostic;
use owo_colors::{OwoColorize, Stream::Stdout}; use owo_colors::{OwoColorize, Stream::Stdout};
use std::{fmt, ops::Range, sync::Arc}; use std::{
fmt::{self, Display},
ops::Range,
sync::Arc,
};
use vec1::Vec1; use vec1::Vec1;
pub const ASSERT_VARIABLE: &str = "_try"; pub const ASSERT_VARIABLE: &str = "_try";

View File

@ -5,8 +5,9 @@ use vec1::Vec1;
use crate::{ use crate::{
ast::{ ast::{
self, Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg, self, Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
DefinitionLocation, IfBranch, ParsedCallArg, Pattern, RecordUpdateSpread, Span, TraceKind, DefinitionLocation, IfBranch, LogicalOpChainKind, ParsedCallArg, Pattern,
TypedClause, TypedRecordUpdateArg, UnOp, UntypedClause, UntypedRecordUpdateArg, RecordUpdateSpread, Span, TraceKind, TypedClause, TypedRecordUpdateArg, UnOp,
UntypedClause, UntypedRecordUpdateArg,
}, },
builtins::void, builtins::void,
parser::token::Base, parser::token::Base,

View File

@ -1,6 +1,6 @@
use super::Type; use super::Type;
use crate::{ use crate::{
ast::{Annotation, BinOp, CallArg, Span, UntypedPattern}, ast::{Annotation, BinOp, CallArg, LogicalOpChainKind, Span, UntypedPattern},
expr::{self, UntypedExpr}, expr::{self, UntypedExpr},
format::Formatter, format::Formatter,
levenshtein, levenshtein,
@ -55,7 +55,22 @@ impl Diagnostic for UnknownLabels {
#[derive(Debug, thiserror::Error, Diagnostic, Clone)] #[derive(Debug, thiserror::Error, Diagnostic, Clone)]
pub enum Error { pub enum Error {
#[error("I discovered a type cast from Data without an annotation")] #[error("I discovered an {} chain with less than 2 expressions.", op.if_supports_color(Stdout, |s| s.purple()))]
#[diagnostic(code("illegal::logical_op_chain"))]
#[diagnostic(help(
"Logical {}/{} chains require at least 2 expressions. You are missing {}.",
"and".if_supports_color(Stdout, |s| s.purple()),
"or".if_supports_color(Stdout, |s| s.purple()),
missing
))]
LogicalOpChainMissingExpr {
op: LogicalOpChainKind,
#[label]
location: Span,
missing: u8,
},
#[error("I discovered a type cast from Data without an annotation.")]
#[diagnostic(code("illegal::type_cast"))] #[diagnostic(code("illegal::type_cast"))]
#[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))] #[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))]
CastDataNoAnn { CastDataNoAnn {

View File

@ -4,10 +4,10 @@ use vec1::Vec1;
use crate::{ use crate::{
ast::{ ast::{
Annotation, Arg, ArgName, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg, Annotation, Arg, ArgName, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
ClauseGuard, Constant, IfBranch, RecordUpdateSpread, Span, TraceKind, Tracing, TypedArg, ClauseGuard, Constant, IfBranch, LogicalOpChainKind, RecordUpdateSpread, Span, TraceKind,
TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch, TypedPattern, Tracing, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch,
TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard, UntypedIfBranch, TypedPattern, TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard,
UntypedPattern, UntypedRecordUpdateArg, UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg,
}, },
builtins::{bool, byte_array, function, int, list, string, tuple}, builtins::{bool, byte_array, function, int, list, string, tuple},
expr::{FnStyle, TypedExpr, UntypedExpr}, expr::{FnStyle, TypedExpr, UntypedExpr},
@ -213,6 +213,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
location, value, .. location, value, ..
} => Ok(self.infer_string(value, location)), } => Ok(self.infer_string(value, location)),
UntypedExpr::LogicalOpChain {
kind,
expressions,
location,
} => self.infer_logical_op_chain(kind, expressions, location),
UntypedExpr::PipeLine { expressions, .. } => self.infer_pipeline(expressions), UntypedExpr::PipeLine { expressions, .. } => self.infer_pipeline(expressions),
UntypedExpr::Fn { UntypedExpr::Fn {
@ -1571,6 +1577,52 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
} }
} }
fn infer_logical_op_chain(
&mut self,
kind: LogicalOpChainKind,
expressions: Vec<UntypedExpr>,
location: Span,
) -> Result<TypedExpr, Error> {
let mut typed_expressions = vec![];
for expression in expressions {
let typed_expression = self.infer(expression)?;
self.unify(
bool(),
typed_expression.tipo(),
typed_expression.location(),
false,
)?;
typed_expressions.push(typed_expression);
}
if typed_expressions.len() < 2 {
return Err(Error::LogicalOpChainMissingExpr {
op: kind,
location,
missing: 2 - typed_expressions.len() as u8,
});
}
let name: BinOp = kind.into();
let chain = typed_expressions
.into_iter()
.rev()
.reduce(|acc, typed_expression| TypedExpr::BinOp {
location: Span::empty(),
tipo: bool(),
name,
left: typed_expression.into(),
right: acc.into(),
})
.expect("should have at least two");
Ok(chain)
}
fn infer_pipeline(&mut self, expressions: Vec1<UntypedExpr>) -> Result<TypedExpr, Error> { fn infer_pipeline(&mut self, expressions: Vec1<UntypedExpr>) -> Result<TypedExpr, Error> {
PipeTyper::infer(self, expressions) PipeTyper::infer(self, expressions)
} }
@ -1927,6 +1979,7 @@ fn assert_no_assignment(expr: &UntypedExpr) -> Result<(), Error> {
| UntypedExpr::TupleIndex { .. } | UntypedExpr::TupleIndex { .. }
| UntypedExpr::UnOp { .. } | UntypedExpr::UnOp { .. }
| UntypedExpr::Var { .. } | UntypedExpr::Var { .. }
| UntypedExpr::LogicalOpChain { .. }
| UntypedExpr::TraceIfFalse { .. } | UntypedExpr::TraceIfFalse { .. }
| UntypedExpr::When { .. } => Ok(()), | UntypedExpr::When { .. } => Ok(()),
} }