use crate::arena::{ContextStack, DataValue};
use crate::operators::{NanForm, nan_error};
use crate::{Engine, Result};
enum LooseEqualsResult {
Equal,
NotEqual,
Incompatible,
}
fn loose_equals_core(left: &DataValue<'_>, right: &DataValue<'_>) -> LooseEqualsResult {
use LooseEqualsResult::*;
match (left, right) {
(DataValue::Null, DataValue::Null) => Equal,
(DataValue::Bool(a), DataValue::Bool(b)) => {
if a == b {
Equal
} else {
NotEqual
}
}
(DataValue::String(a), DataValue::String(b)) => {
if a == b {
Equal
} else {
NotEqual
}
}
(DataValue::Number(a), DataValue::Number(b)) => {
if a == b {
Equal
} else {
NotEqual
}
}
(DataValue::Number(n), DataValue::String(s))
| (DataValue::String(s), DataValue::Number(n)) => match crate::arena::parse_finite(s) {
Some(s_f) if n.as_f64() == s_f => match (n.as_i64(), s.parse::<i64>()) {
(Some(i), Ok(si)) if i.unsigned_abs() >= 1 << 53 => {
if i == si {
Equal
} else {
NotEqual
}
}
_ => Equal,
},
Some(_) => NotEqual,
None => Incompatible,
},
(DataValue::Number(n), DataValue::Bool(b)) | (DataValue::Bool(b), DataValue::Number(n)) => {
if n.as_f64() == (if *b { 1.0 } else { 0.0 }) {
Equal
} else {
NotEqual
}
}
(DataValue::String(s), DataValue::Bool(b)) | (DataValue::Bool(b), DataValue::String(s)) => {
if *s == (if *b { "true" } else { "false" }) {
Equal
} else {
NotEqual
}
}
(DataValue::Null, DataValue::Number(n)) | (DataValue::Number(n), DataValue::Null) => {
if n.as_f64() == 0.0 { Equal } else { NotEqual }
}
(DataValue::Null, DataValue::Bool(b)) | (DataValue::Bool(b), DataValue::Null) => {
if !*b {
Equal
} else {
NotEqual
}
}
(DataValue::Null, DataValue::String(s)) | (DataValue::String(s), DataValue::Null) => {
if s.is_empty() { Equal } else { NotEqual }
}
(DataValue::Array(_), _) | (_, DataValue::Array(_))
if !matches!((left, right), (DataValue::Array(_), DataValue::Array(_))) =>
{
Incompatible
}
(DataValue::Object(_), _) | (_, DataValue::Object(_))
if !matches!((left, right), (DataValue::Object(_), DataValue::Object(_))) =>
{
Incompatible
}
(DataValue::Array(a), DataValue::Array(b)) => {
if a == b {
Equal
} else {
Incompatible
}
}
#[cfg(feature = "tensor")]
(DataValue::Tensor(a), DataValue::Tensor(b)) => {
if a == b {
Equal
} else {
NotEqual
}
}
#[cfg(feature = "tensor")]
(DataValue::Tensor(_), _) | (_, DataValue::Tensor(_)) => Incompatible,
_ => NotEqual,
}
}
pub(super) fn loose_equals(
left: &DataValue<'_>,
right: &DataValue<'_>,
engine: &Engine,
ctx: &mut ContextStack<'_>,
) -> Result<bool> {
match loose_equals_core(left, right) {
LooseEqualsResult::Equal => Ok(true),
LooseEqualsResult::NotEqual => Ok(false),
LooseEqualsResult::Incompatible => incompatible(engine, ctx),
}
}
pub(super) fn incompatible(engine: &Engine, ctx: &mut ContextStack<'_>) -> Result<bool> {
if engine.config().loose_equality_errors {
Err(nan_error(NanForm::InvalidArguments, ctx))
} else {
Ok(false)
}
}