use crate::eval::coercion::{to_number, to_string_val};
use crate::types::{ErrorKind, Value};
use super::Registry;
fn to_number_arith(v: Value) -> Result<f64, Value> {
match &v {
Value::Text(s) if s.is_empty() => return Ok(0.0),
_ => {}
}
to_number(v)
}
fn check_exact(args: &[Value], n: usize) -> Option<Value> {
if args.len() != n {
Some(Value::Error(ErrorKind::NA))
} else {
None
}
}
pub fn add_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
let a = match to_number_arith(args[0].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let b = match to_number_arith(args[1].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let result = a + b;
if !result.is_finite() {
return Value::Error(ErrorKind::Num);
}
Value::Number(result)
}
pub fn minus_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
let a = match to_number_arith(args[0].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let b = match to_number_arith(args[1].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let result = a - b;
if !result.is_finite() {
return Value::Error(ErrorKind::Num);
}
Value::Number(result)
}
pub fn multiply_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
let a = match to_number_arith(args[0].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let b = match to_number_arith(args[1].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let result = a * b;
if !result.is_finite() {
return Value::Error(ErrorKind::Num);
}
Value::Number(result)
}
pub fn divide_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
let a = match to_number_arith(args[0].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let b = match to_number_arith(args[1].clone()) {
Ok(v) => v,
Err(e) => return e,
};
if b == 0.0 {
return Value::Error(ErrorKind::DivByZero);
}
let result = a / b;
if !result.is_finite() {
return Value::Error(ErrorKind::Num);
}
Value::Number(result)
}
fn type_rank(v: &Value) -> u8 {
match v {
Value::Number(_) | Value::Empty => 0,
Value::Text(_) => 1,
Value::Bool(_) => 2,
_ => 255,
}
}
fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
match (a, b) {
(Value::Number(x), Value::Number(y)) => x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal),
(Value::Text(x), Value::Text(y)) => x.to_lowercase().cmp(&y.to_lowercase()),
(Value::Bool(x), Value::Bool(y)) => x.cmp(y),
_ => type_rank(a).cmp(&type_rank(b)),
}
}
fn same_type(a: &Value, b: &Value) -> bool {
matches!(
(a, b),
(Value::Number(_), Value::Number(_))
| (Value::Text(_), Value::Text(_))
| (Value::Bool(_), Value::Bool(_))
| (Value::Empty, Value::Empty)
)
}
pub fn eq_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
let (a, b) = (&args[0], &args[1]);
if !same_type(a, b) {
return Value::Bool(false);
}
Value::Bool(compare_values(a, b) == std::cmp::Ordering::Equal)
}
pub fn ne_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
let (a, b) = (&args[0], &args[1]);
if !same_type(a, b) {
return Value::Bool(true);
}
Value::Bool(compare_values(a, b) != std::cmp::Ordering::Equal)
}
pub fn gt_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
Value::Bool(compare_values(&args[0], &args[1]) == std::cmp::Ordering::Greater)
}
pub fn gte_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
Value::Bool(compare_values(&args[0], &args[1]) != std::cmp::Ordering::Less)
}
pub fn lt_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
Value::Bool(compare_values(&args[0], &args[1]) == std::cmp::Ordering::Less)
}
pub fn lte_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
Value::Bool(compare_values(&args[0], &args[1]) != std::cmp::Ordering::Greater)
}
pub fn pow_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
let base = match to_number(args[0].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let exp = match to_number(args[1].clone()) {
Ok(v) => v,
Err(e) => return e,
};
let result = base.powf(exp);
if !result.is_finite() {
return Value::Error(ErrorKind::Num);
}
Value::Number(result)
}
pub fn concat_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 2) {
return e;
}
let a = match to_string_val(args[0].clone()) {
Ok(s) => s,
Err(e) => return e,
};
let b = match to_string_val(args[1].clone()) {
Ok(s) => s,
Err(e) => return e,
};
let result = format!("{}{}", a, b);
if let Ok(n) = result.parse::<f64>() {
return Value::Number(n);
}
Value::Text(result)
}
pub fn uminus_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 1) {
return e;
}
let n = match to_number(args[0].clone()) {
Ok(v) => v,
Err(e) => return e,
};
Value::Number(-n)
}
pub fn uplus_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 1) {
return e;
}
match &args[0] {
Value::Text(s) => {
if let Ok(n) = s.parse::<f64>() {
Value::Number(n)
} else {
args[0].clone()
}
}
_ => args[0].clone(),
}
}
pub fn unary_percent_fn(args: &[Value]) -> Value {
if let Some(e) = check_exact(args, 1) {
return e;
}
let n = match to_number(args[0].clone()) {
Ok(v) => v,
Err(e) => return e,
};
Value::Number(n / 100.0)
}
pub fn register_operator(registry: &mut Registry) {
registry.register_eager("ADD", add_fn);
registry.register_eager("MINUS", minus_fn);
registry.register_eager("MULTIPLY", multiply_fn);
registry.register_eager("DIVIDE", divide_fn);
registry.register_eager("EQ", eq_fn);
registry.register_eager("NE", ne_fn);
registry.register_eager("GT", gt_fn);
registry.register_eager("GTE", gte_fn);
registry.register_eager("LT", lt_fn);
registry.register_eager("LTE", lte_fn);
registry.register_eager("POW", pow_fn);
registry.register_eager("CONCAT", concat_fn);
registry.register_eager("UMINUS", uminus_fn);
registry.register_eager("UPLUS", uplus_fn);
registry.register_eager("UNARY_PERCENT", unary_percent_fn);
}