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 owo_colors::{OwoColorize, Stream::Stdout};
use std::{fmt, ops::Range, sync::Arc};
use std::{
fmt::{self, Display},
ops::Range,
sync::Arc,
};
use vec1::Vec1;
pub const ASSERT_VARIABLE: &str = "_try";

View File

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

View File

@ -1,6 +1,6 @@
use super::Type;
use crate::{
ast::{Annotation, BinOp, CallArg, Span, UntypedPattern},
ast::{Annotation, BinOp, CallArg, LogicalOpChainKind, Span, UntypedPattern},
expr::{self, UntypedExpr},
format::Formatter,
levenshtein,
@ -55,7 +55,22 @@ impl Diagnostic for UnknownLabels {
#[derive(Debug, thiserror::Error, Diagnostic, Clone)]
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(help("Try adding an annotation...\n\n{}", format_suggestion(value)))]
CastDataNoAnn {

View File

@ -4,10 +4,10 @@ use vec1::Vec1;
use crate::{
ast::{
Annotation, Arg, ArgName, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
ClauseGuard, Constant, IfBranch, RecordUpdateSpread, Span, TraceKind, Tracing, TypedArg,
TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch, TypedPattern,
TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard, UntypedIfBranch,
UntypedPattern, UntypedRecordUpdateArg,
ClauseGuard, Constant, IfBranch, LogicalOpChainKind, RecordUpdateSpread, Span, TraceKind,
Tracing, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch,
TypedPattern, TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard,
UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg,
},
builtins::{bool, byte_array, function, int, list, string, tuple},
expr::{FnStyle, TypedExpr, UntypedExpr},
@ -213,6 +213,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
location, value, ..
} => 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::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> {
PipeTyper::infer(self, expressions)
}
@ -1927,6 +1979,7 @@ fn assert_no_assignment(expr: &UntypedExpr) -> Result<(), Error> {
| UntypedExpr::TupleIndex { .. }
| UntypedExpr::UnOp { .. }
| UntypedExpr::Var { .. }
| UntypedExpr::LogicalOpChain { .. }
| UntypedExpr::TraceIfFalse { .. }
| UntypedExpr::When { .. } => Ok(()),
}