use crate::ast::*;
use super::scope::InferredType;
pub(super) fn infer_binary_op_type(
op: &str,
left: &InferredType,
right: &InferredType,
) -> InferredType {
match op {
"==" | "!=" | "<" | ">" | "<=" | ">=" | "&&" | "||" | "in" | "not_in" => {
Some(TypeExpr::Named("bool".into()))
}
"+" => match (left, right) {
(Some(TypeExpr::Named(l)), Some(TypeExpr::Named(r))) => {
match (l.as_str(), r.as_str()) {
("int", "int") => Some(TypeExpr::Named("int".into())),
("float", _) | (_, "float") => Some(TypeExpr::Named("float".into())),
("string", "string") => Some(TypeExpr::Named("string".into())),
("list", "list") => Some(TypeExpr::Named("list".into())),
("dict", "dict") => Some(TypeExpr::Named("dict".into())),
_ => None,
}
}
_ => None,
},
"-" | "/" | "%" => match (left, right) {
(Some(TypeExpr::Named(l)), Some(TypeExpr::Named(r))) => {
match (l.as_str(), r.as_str()) {
("int", "int") => Some(TypeExpr::Named("int".into())),
("float", _) | (_, "float") => Some(TypeExpr::Named("float".into())),
_ => None,
}
}
_ => None,
},
"**" => match (left, right) {
(Some(TypeExpr::Named(l)), Some(TypeExpr::Named(r))) => {
match (l.as_str(), r.as_str()) {
("int", "int") => Some(TypeExpr::Named("int".into())),
("float", _) | (_, "float") => Some(TypeExpr::Named("float".into())),
_ => None,
}
}
_ => None,
},
"*" => match (left, right) {
(Some(TypeExpr::Named(l)), Some(TypeExpr::Named(r))) => {
match (l.as_str(), r.as_str()) {
("string", "int") | ("int", "string") => Some(TypeExpr::Named("string".into())),
("int", "int") => Some(TypeExpr::Named("int".into())),
("float", _) | (_, "float") => Some(TypeExpr::Named("float".into())),
_ => None,
}
}
_ => None,
},
"??" => match (left, right) {
(Some(TypeExpr::Union(members)), _) => {
let non_nil: Vec<_> = members
.iter()
.filter(|m| !matches!(m, TypeExpr::Named(n) if n == "nil"))
.cloned()
.collect();
if non_nil.len() == 1 {
Some(non_nil[0].clone())
} else if non_nil.is_empty() {
right.clone()
} else {
Some(TypeExpr::Union(non_nil))
}
}
(Some(TypeExpr::Named(n)), _) if n == "nil" => right.clone(),
(Some(l), _) => Some(l.clone()),
(None, _) => right.clone(),
},
"|>" => None,
_ => None,
}
}