use super::types::DataType;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperatorKind {
Infix,
Prefix,
Postfix,
}
#[derive(Debug, Clone, Copy)]
pub struct OperatorEntry {
pub name: &'static str,
pub lhs_type: DataType,
pub rhs_type: DataType,
pub return_type: DataType,
pub kind: OperatorKind,
}
const fn infix(name: &'static str, lhs: DataType, rhs: DataType, ret: DataType) -> OperatorEntry {
OperatorEntry {
name,
lhs_type: lhs,
rhs_type: rhs,
return_type: ret,
kind: OperatorKind::Infix,
}
}
const fn prefix(name: &'static str, operand: DataType, ret: DataType) -> OperatorEntry {
OperatorEntry {
name,
lhs_type: DataType::Nullable,
rhs_type: operand,
return_type: ret,
kind: OperatorKind::Prefix,
}
}
pub const OPERATOR_CATALOG: &[OperatorEntry] = &[
infix("+", DataType::Integer, DataType::Integer, DataType::Integer),
infix("+", DataType::Integer, DataType::Float, DataType::Float),
infix("+", DataType::Float, DataType::Integer, DataType::Float),
infix("+", DataType::Float, DataType::Float, DataType::Float),
infix("+", DataType::BigInt, DataType::BigInt, DataType::BigInt),
infix("+", DataType::Decimal, DataType::Decimal, DataType::Decimal),
infix("-", DataType::Integer, DataType::Integer, DataType::Integer),
infix("-", DataType::Float, DataType::Float, DataType::Float),
infix("-", DataType::BigInt, DataType::BigInt, DataType::BigInt),
infix("-", DataType::Decimal, DataType::Decimal, DataType::Decimal),
prefix("-", DataType::Integer, DataType::Integer),
prefix("-", DataType::Float, DataType::Float),
prefix("-", DataType::BigInt, DataType::BigInt),
prefix("-", DataType::Decimal, DataType::Decimal),
infix("*", DataType::Integer, DataType::Integer, DataType::Integer),
infix("*", DataType::Float, DataType::Float, DataType::Float),
infix("*", DataType::BigInt, DataType::BigInt, DataType::BigInt),
infix("/", DataType::Integer, DataType::Integer, DataType::Float),
infix("/", DataType::Float, DataType::Float, DataType::Float),
infix("/", DataType::BigInt, DataType::BigInt, DataType::Float),
infix("%", DataType::Integer, DataType::Integer, DataType::Integer),
infix("%", DataType::BigInt, DataType::BigInt, DataType::BigInt),
infix("||", DataType::Text, DataType::Text, DataType::Text),
infix("=", DataType::Integer, DataType::Integer, DataType::Boolean),
infix("=", DataType::Float, DataType::Float, DataType::Boolean),
infix("=", DataType::Text, DataType::Text, DataType::Boolean),
infix("=", DataType::Boolean, DataType::Boolean, DataType::Boolean),
infix("=", DataType::Uuid, DataType::Uuid, DataType::Boolean),
infix(
"=",
DataType::Timestamp,
DataType::Timestamp,
DataType::Boolean,
),
infix(
"<>",
DataType::Integer,
DataType::Integer,
DataType::Boolean,
),
infix("<>", DataType::Float, DataType::Float, DataType::Boolean),
infix("<>", DataType::Text, DataType::Text, DataType::Boolean),
infix("<", DataType::Integer, DataType::Integer, DataType::Boolean),
infix("<", DataType::Float, DataType::Float, DataType::Boolean),
infix("<", DataType::Text, DataType::Text, DataType::Boolean),
infix(
"<",
DataType::Timestamp,
DataType::Timestamp,
DataType::Boolean,
),
infix(
"<=",
DataType::Integer,
DataType::Integer,
DataType::Boolean,
),
infix("<=", DataType::Float, DataType::Float, DataType::Boolean),
infix("<=", DataType::Text, DataType::Text, DataType::Boolean),
infix(">", DataType::Integer, DataType::Integer, DataType::Boolean),
infix(">", DataType::Float, DataType::Float, DataType::Boolean),
infix(">", DataType::Text, DataType::Text, DataType::Boolean),
infix(
">=",
DataType::Integer,
DataType::Integer,
DataType::Boolean,
),
infix(">=", DataType::Float, DataType::Float, DataType::Boolean),
infix(">=", DataType::Text, DataType::Text, DataType::Boolean),
infix(
"AND",
DataType::Boolean,
DataType::Boolean,
DataType::Boolean,
),
infix(
"OR",
DataType::Boolean,
DataType::Boolean,
DataType::Boolean,
),
prefix("NOT", DataType::Boolean, DataType::Boolean),
];
pub fn lookup(name: &str) -> Vec<&'static OperatorEntry> {
OPERATOR_CATALOG.iter().filter(|e| e.name == name).collect()
}
pub fn resolve(
name: &str,
kind: OperatorKind,
lhs: DataType,
rhs: DataType,
) -> Option<&'static OperatorEntry> {
let candidates: Vec<&'static OperatorEntry> = OPERATOR_CATALOG
.iter()
.filter(|e| e.name == name && e.kind == kind)
.collect();
if candidates.is_empty() {
return None;
}
let mut best: Option<(usize, &'static OperatorEntry)> = None;
for entry in candidates {
let lhs_match = match kind {
OperatorKind::Infix => (entry.lhs_type == lhs) as usize,
OperatorKind::Prefix | OperatorKind::Postfix => 0,
};
let rhs_match = (entry.rhs_type == rhs) as usize;
let score = lhs_match + rhs_match;
if score == 0 && OPERATOR_CATALOG.iter().filter(|e| e.name == name).count() > 1 {
continue;
}
match best {
None => best = Some((score, entry)),
Some((prev_score, prev_entry)) => {
if score > prev_score
|| (score == prev_score
&& entry.return_type.is_preferred()
&& !prev_entry.return_type.is_preferred())
{
best = Some((score, entry));
}
}
}
}
best.map(|(_, entry)| entry)
}