use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use itertools::{EitherOrBoth, Itertools};
use crate::check::context::{arg, Context, LookupFunction};
use crate::check::context::arg::FunctionArg;
use crate::check::context::clss::Class;
use crate::check::context::function::generic::GenericFunction;
use crate::check::name::{Empty, IsSuperSet, Substitute};
use crate::check::name::Name;
use crate::check::name::string_name::StringName;
use crate::check::result::{TypeErr, TypeResult};
use crate::common::delimit::comma_delm;
use crate::common::position::Position;
pub const INIT: &str = "init";
pub const PRINT: &str = "print";
pub const ADD: &str = "+";
pub const DIV: &str = "/";
pub const EQ: &str = "=";
pub const FDIV: &str = "//";
pub const GE: &str = ">";
pub const GEQ: &str = ">=";
pub const LE: &str = "<";
pub const LEQ: &str = "<=";
pub const MOD: &str = "mod";
pub const MUL: &str = "*";
pub const NEQ: &str = "/=";
pub const POW: &str = "^";
pub const SUB: &str = "-";
pub const SQRT: &str = "sqrt";
pub const STR: &str = python::STR;
pub const TRUTHY: &str = python::TRUTHY;
pub const NEXT: &str = python::NEXT;
pub const ITER: &str = python::ITER;
pub mod union;
pub mod generic;
pub mod python;
#[derive(Debug, Clone, Eq)]
pub struct Function {
pub is_py_type: bool,
pub name: StringName,
pub self_mutable: Option<bool>,
pub pure: bool,
pub arguments: Vec<FunctionArg>,
pub raises: Name,
pub in_class: Option<StringName>,
pub ret_ty: Name,
}
impl LookupFunction<&StringName, Function> for Context {
fn function(&self, function: &StringName, pos: Position) -> TypeResult<Function> {
let generics = HashMap::new();
if let Some(generic_fun) = self.functions.iter().find(|c| &c.name == function) {
Function::try_from((generic_fun, &generics, pos))
} else if let Some(generic_class) = self.classes.iter().find(|c| &c.name == function) {
let class = Class::try_from((generic_class, &generics, pos))?;
Ok(class.constructor(true))
} else {
let msg = format!("Function {function} is undefined.");
Err(vec![TypeErr::new(pos, &msg)])
}
}
}
impl Hash for Function {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.arguments.hash(state);
self.ret_ty.hash(state)
}
}
impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.arguments == other.arguments && self.ret_ty == other.ret_ty
}
}
impl Display for Function {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let ret =
if self.ret_ty.is_empty() { String::new() } else { format!(" -> {}", self.ret_ty) };
let raises = if self.raises.is_empty() {
String::new()
} else {
format!(" raises [{}]", &self.raises)
};
write!(f, "{: >8} : ({}){ret}{raises}", self.name, comma_delm(&self.arguments))
}
}
impl TryFrom<(&GenericFunction, &HashMap<Name, Name>, Position)> for Function {
type Error = Vec<TypeErr>;
fn try_from(
(fun, generics, pos): (&GenericFunction, &HashMap<Name, Name>, Position)
) -> Result<Self, Self::Error> {
let arguments: Vec<FunctionArg> = fun
.arguments
.iter()
.map(|arg| FunctionArg::try_from((arg, generics, pos)))
.collect::<Result<_, _>>()?;
Ok(Function {
is_py_type: fun.is_py_type,
name: fun.name.substitute(generics, pos)?,
self_mutable: {
let function_arg = arguments.iter().find_map(|a| {
if a.name == arg::SELF {
Some(a.clone())
} else {
None
}
});
function_arg.map(|a| a.mutable)
},
pure: fun.pure,
arguments,
raises: fun.raises.substitute(generics, pos)?,
in_class: match &fun.in_class {
Some(in_class) => Some(in_class.substitute(generics, pos)?),
None => None
},
ret_ty: match &fun.ret_ty {
Some(ty) => ty.substitute(generics, pos)?,
None => Name::empty()
},
})
}
}
impl Function {
pub fn args_compatible(
&self,
args: &[Name],
ctx: &Context,
pos: Position,
) -> TypeResult<()> {
for pair in self.arguments.iter().zip_longest(args) {
match pair {
EitherOrBoth::Both(fun_param, arg) =>
if let Some(arg_ty) = &fun_param.ty {
if !arg_ty.is_superset_of(arg, ctx, pos)? {
let msg = format!(
"'{arg}' given to argument {fun_param}, which expected a '{arg_ty}'"
);
return Err(vec![TypeErr::new(pos, &msg)]);
}
} else {
let msg = format!("Type of function parameter {fun_param} unknown.");
return Err(vec![TypeErr::new(pos, &msg)]);
},
EitherOrBoth::Left(fun_param) =>
if !fun_param.has_default {
let msg = format!("Expected an argument for {fun_param}.");
return Err(vec![TypeErr::new(pos, &msg)]);
},
EitherOrBoth::Right(_) => {
let msg = format!(
"{} arguments given to {self}\nExpected at most {} arguments.",
args.len(),
self.arguments.len()
);
return Err(vec![TypeErr::new(pos, &msg)]);
}
}
}
Ok(())
}
pub fn simple_fun(
name: &StringName,
self_arg: &Name,
ret_ty: &Name,
pos: Position,
) -> TypeResult<Function> {
if self_arg.is_empty() {
let msg = format!("'{}' self argument of '{name}' cannot be empty", arg::SELF);
return Err(vec![TypeErr::new(pos, &msg)]);
}
Ok(Function {
is_py_type: false,
name: name.clone(),
self_mutable: None,
pure: false,
arguments: vec![FunctionArg {
is_py_type: false,
name: String::from(arg::SELF),
has_default: false,
vararg: false,
mutable: false,
ty: Some(self_arg.clone()),
}],
raises: Name::empty(),
in_class: None,
ret_ty: ret_ty.clone(),
})
}
}