feat(tipo): inference for and/or chains
This commit is contained in:
parent
ab3a418b9c
commit
e4ef386c44
|
@ -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";
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(()),
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue