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 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";
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(()),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue