use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::hash::Hash;
use allocative::Allocative;
use dupe::Dupe;
use crate::codemap::Span;
use crate::codemap::Spanned;
use crate::typing::custom::TyCustomImpl;
use crate::typing::error::TypingOrInternalError;
use crate::typing::small_arc_vec_or_static::SmallArcVec1OrStatic;
use crate::typing::Ty;
use crate::typing::TyBasic;
use crate::typing::TypingBinOp;
use crate::typing::TypingOracleCtx;
use crate::values::layout::heap::profile::arc_str::ArcStr;
use crate::values::typing::type_compiled::alloc::TypeMatcherAlloc;
#[derive(Debug)]
pub enum Arg<'a> {
Pos(Ty),
Name(&'a str, Ty),
Args(Ty),
Kwargs(Ty),
}
#[derive(Debug, Clone, Dupe, PartialEq, Eq, Hash, PartialOrd, Ord, Allocative)]
pub(crate) enum ParamMode {
PosOnly,
PosOrName(ArcStr),
NameOnly(ArcStr),
Args,
Kwargs,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Allocative)]
pub struct Param {
pub(crate) mode: ParamMode,
pub(crate) optional: bool,
pub(crate) ty: Ty,
}
impl Param {
pub fn pos_only(ty: Ty) -> Self {
Self {
mode: ParamMode::PosOnly,
optional: false,
ty,
}
}
pub fn name_only(name: &str, ty: Ty) -> Self {
Self {
mode: ParamMode::NameOnly(ArcStr::from(name)),
optional: false,
ty,
}
}
pub fn pos_or_name(name: &str, ty: Ty) -> Self {
Self {
mode: ParamMode::PosOrName(ArcStr::from(name)),
optional: false,
ty,
}
}
pub fn optional(self) -> Self {
Self {
optional: true,
..self
}
}
pub const fn args(ty: Ty) -> Self {
Self {
mode: ParamMode::Args,
optional: true,
ty,
}
}
pub const fn kwargs(ty: Ty) -> Self {
Self {
mode: ParamMode::Kwargs,
optional: true,
ty,
}
}
pub(crate) fn allows_pos(&self) -> bool {
match self.mode {
ParamMode::PosOnly | ParamMode::PosOrName(_) | ParamMode::Args => true,
ParamMode::NameOnly(_) | ParamMode::Kwargs => false,
}
}
pub(crate) fn allows_many(&self) -> bool {
match self.mode {
ParamMode::Args | ParamMode::Kwargs => true,
_ => false,
}
}
pub fn name(&self) -> &str {
match &self.mode {
ParamMode::PosOnly => "_",
ParamMode::PosOrName(x) => x,
ParamMode::NameOnly(x) => x,
ParamMode::Args => "*args",
ParamMode::Kwargs => "**kwargs",
}
}
}
pub trait TyCustomFunctionImpl:
Debug + Eq + Ord + Hash + Allocative + Send + Sync + 'static
{
fn has_type_attr(&self) -> bool {
false
}
fn validate_call(
&self,
span: Span,
args: &[Spanned<Arg>],
oracle: TypingOracleCtx,
) -> Result<Ty, TypingOrInternalError>;
fn as_function(&self) -> Option<&TyFunction> {
None
}
}
#[derive(
Allocative,
Eq,
PartialEq,
Hash,
Ord,
PartialOrd,
Debug,
derive_more::Display
)]
#[display(fmt = "\"function\"")]
pub struct TyCustomFunction<F: TyCustomFunctionImpl>(pub F);
impl<F: TyCustomFunctionImpl> TyCustomImpl for TyCustomFunction<F> {
fn as_name(&self) -> Option<&str> {
Some("function")
}
fn validate_call(
&self,
span: Span,
args: &[Spanned<Arg>],
oracle: TypingOracleCtx,
) -> Result<Ty, TypingOrInternalError> {
self.0.validate_call(span, args, oracle)
}
fn is_callable(&self) -> bool {
true
}
fn as_function(&self) -> Option<&TyFunction> {
self.0.as_function()
}
fn bin_op(
&self,
bin_op: TypingBinOp,
_rhs: &TyBasic,
_ctx: &TypingOracleCtx,
) -> Result<Ty, ()> {
match bin_op {
TypingBinOp::BitOr if self.0.has_type_attr() => {
Ok(Ty::any())
}
_ => Err(()),
}
}
fn index(&self, _item: &TyBasic, _ctx: &TypingOracleCtx) -> Result<Ty, ()> {
Ok(Ty::any())
}
fn attribute(&self, attr: &str) -> Result<Ty, ()> {
if attr == "type" && self.0.has_type_attr() {
Ok(Ty::string())
} else {
Err(())
}
}
fn matcher<T: TypeMatcherAlloc>(&self, factory: T) -> T::Result {
factory.callable()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Allocative)]
pub struct TyFunction {
pub(crate) type_attr: Option<Ty>,
pub(crate) params: SmallArcVec1OrStatic<Param>,
pub(crate) result: Ty,
}
impl TyFunction {
pub fn new_with_type_attr(params: Vec<Param>, result: Ty, type_attr: Ty) -> Self {
TyFunction {
type_attr: Some(type_attr),
params: Self::maybe_intern_params(params),
result,
}
}
pub fn new(params: Vec<Param>, result: Ty) -> Self {
TyFunction {
type_attr: None,
params: Self::maybe_intern_params(params),
result,
}
}
fn maybe_intern_params(params: Vec<Param>) -> SmallArcVec1OrStatic<Param> {
if params.as_slice() == Self::any_params() {
SmallArcVec1OrStatic::new_static(Self::any_params())
} else {
SmallArcVec1OrStatic::clone_from_slice(¶ms)
}
}
fn any_params() -> &'static [Param] {
static ANY_PARAMS: [Param; 2] = [Param::args(Ty::any()), Param::kwargs(Ty::any())];
&ANY_PARAMS
}
pub(crate) fn _any() -> TyFunction {
TyFunction {
type_attr: None,
params: SmallArcVec1OrStatic::new_static(Self::any_params()),
result: Ty::any(),
}
}
}
impl Display for TyFunction {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let TyFunction { params, result, .. } = self;
write!(f, "def(")?;
let mut first = true;
for param in params.iter() {
if !first {
write!(f, ", ")?;
first = false;
}
let opt = if param.optional { "=.." } else { "" };
match ¶m.mode {
ParamMode::PosOnly => write!(f, "#: {}{}", param.ty, opt)?,
ParamMode::PosOrName(name) => write!(f, "#{}: {}{}", name, param.ty, opt)?,
ParamMode::NameOnly(name) => write!(f, "{}: {}{}", name, param.ty, opt)?,
ParamMode::Args => write!(f, "*args: {}", param.ty)?,
ParamMode::Kwargs => write!(f, "**kwargs: {}", param.ty)?,
}
}
write!(f, ") -> {}", result)
}
}
impl TyCustomFunctionImpl for TyFunction {
fn has_type_attr(&self) -> bool {
self.type_attr.is_some()
}
fn validate_call(
&self,
span: Span,
args: &[Spanned<Arg>],
oracle: TypingOracleCtx,
) -> Result<Ty, TypingOrInternalError> {
oracle.validate_fn_call(span, self, args)
}
fn as_function(&self) -> Option<&TyFunction> {
Some(self)
}
}