use crate::context::Context;
use crate::functions::FunctionContext;
use crate::ser::SerializationError;
use crate::ExecutionError::NoSuchKey;
use crate::{to_value, ExecutionError};
use cel_parser::{ArithmeticOp, Atom, Expression, Member, RelationOp, UnaryOp};
use chrono::{DateTime, Duration, FixedOffset};
use core::ops;
use serde::{Serialize, Serializer};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::fmt::{Display, Formatter};
use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, PartialEq, Clone)]
pub struct Map {
pub map: Rc<HashMap<Key, Value>>,
}
impl PartialOrd for Map {
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
None
}
}
impl Map {
pub fn get(&self, key: &Key) -> Option<&Value> {
self.map.get(key).or_else(|| {
let converted = match key {
Key::Int(k) => Key::Uint(u64::try_from(*k).ok()?),
Key::Uint(k) => Key::Int(i64::try_from(*k).ok()?),
_ => return None,
};
self.map.get(&converted)
})
}
}
#[derive(Debug, Eq, PartialEq, Hash, Ord, Clone, PartialOrd)]
pub enum Key {
Int(i64),
Uint(u64),
Bool(bool),
String(Arc<String>),
}
impl From<String> for Key {
fn from(v: String) -> Self {
Key::String(v.into())
}
}
impl From<Arc<String>> for Key {
fn from(v: Arc<String>) -> Self {
Key::String(v.clone())
}
}
impl<'a> From<&'a str> for Key {
fn from(v: &'a str) -> Self {
Key::String(Arc::new(v.into()))
}
}
impl From<bool> for Key {
fn from(v: bool) -> Self {
Key::Bool(v)
}
}
impl From<i64> for Key {
fn from(v: i64) -> Self {
Key::Int(v)
}
}
impl From<u64> for Key {
fn from(v: u64) -> Self {
Key::Uint(v)
}
}
impl Serialize for Key {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Key::Int(v) => v.serialize(serializer),
Key::Uint(v) => v.serialize(serializer),
Key::Bool(v) => v.serialize(serializer),
Key::String(v) => v.serialize(serializer),
}
}
}
impl TryInto<Key> for Value {
type Error = Value;
#[inline(always)]
fn try_into(self) -> Result<Key, Self::Error> {
match self {
Value::Int(v) => Ok(Key::Int(v)),
Value::UInt(v) => Ok(Key::Uint(v)),
Value::String(v) => Ok(Key::String(v)),
Value::Bool(v) => Ok(Key::Bool(v)),
_ => Err(self),
}
}
}
impl<K: Into<Key>, V: Into<Value>> From<HashMap<K, V>> for Map {
fn from(map: HashMap<K, V>) -> Self {
let mut new_map = HashMap::new();
for (k, v) in map {
new_map.insert(k.into(), v.into());
}
Map {
map: Rc::new(new_map),
}
}
}
impl Serialize for &Map {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.map.as_ref().serialize(serializer)
}
}
pub trait TryIntoValue {
type Error: std::error::Error + 'static;
fn try_into_value(self) -> Result<Value, Self::Error>;
}
impl<T: Serialize> TryIntoValue for T {
type Error = SerializationError;
fn try_into_value(self) -> Result<Value, Self::Error> {
to_value(self)
}
}
#[derive(Debug, Clone)]
pub enum Value {
List(Arc<Vec<Value>>),
Map(Map),
Function(Arc<String>, Option<Box<Value>>),
Int(i64),
UInt(u64),
Float(f64),
String(Arc<String>),
Bytes(Arc<Vec<u8>>),
Bool(bool),
Duration(Duration),
Timestamp(DateTime<FixedOffset>),
Null,
}
pub enum ValueType {
List,
Map,
Function,
Int,
UInt,
Float,
String,
Bytes,
Bool,
Duration,
Timestamp,
Null,
}
impl Display for ValueType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ValueType::List => write!(f, "list"),
ValueType::Map => write!(f, "map"),
ValueType::Function => write!(f, "function"),
ValueType::Int => write!(f, "int"),
ValueType::UInt => write!(f, "uint"),
ValueType::Float => write!(f, "float"),
ValueType::String => write!(f, "string"),
ValueType::Bytes => write!(f, "bytes"),
ValueType::Bool => write!(f, "bool"),
ValueType::Duration => write!(f, "duration"),
ValueType::Timestamp => write!(f, "timestamp"),
ValueType::Null => write!(f, "null"),
}
}
}
impl Value {
pub fn type_of(&self) -> ValueType {
match self {
Value::List(_) => ValueType::List,
Value::Map(_) => ValueType::Map,
Value::Function(_, _) => ValueType::Function,
Value::Int(_) => ValueType::Int,
Value::UInt(_) => ValueType::UInt,
Value::Float(_) => ValueType::Float,
Value::String(_) => ValueType::String,
Value::Bytes(_) => ValueType::Bytes,
Value::Bool(_) => ValueType::Bool,
Value::Duration(_) => ValueType::Duration,
Value::Timestamp(_) => ValueType::Timestamp,
Value::Null => ValueType::Null,
}
}
pub fn error_expected_type(&self, expected: ValueType) -> ExecutionError {
ExecutionError::UnexpectedType {
got: self.type_of().to_string(),
want: expected.to_string(),
}
}
}
impl From<&Value> for Value {
fn from(value: &Value) -> Self {
value.clone()
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Map(a), Value::Map(b)) => a == b,
(Value::List(a), Value::List(b)) => a == b,
(Value::Function(a1, a2), Value::Function(b1, b2)) => a1 == b1 && a2 == b2,
(Value::Int(a), Value::Int(b)) => a == b,
(Value::UInt(a), Value::UInt(b)) => a == b,
(Value::Float(a), Value::Float(b)) => a == b,
(Value::String(a), Value::String(b)) => a == b,
(Value::Bytes(a), Value::Bytes(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Null, Value::Null) => true,
(Value::Duration(a), Value::Duration(b)) => a == b,
(Value::Timestamp(a), Value::Timestamp(b)) => a == b,
(Value::Int(a), Value::UInt(b)) => a
.to_owned()
.try_into()
.and_then(|a: u64| Ok(a == *b))
.unwrap_or(false),
(Value::Int(a), Value::Float(b)) => (*a as f64) == *b,
(Value::UInt(a), Value::Int(b)) => a
.to_owned()
.try_into()
.and_then(|a: i64| Ok(a == *b))
.unwrap_or(false),
(Value::UInt(a), Value::Float(b)) => (*a as f64) == *b,
(Value::Float(a), Value::Int(b)) => *a == (*b as f64),
(Value::Float(a), Value::UInt(b)) => *a == (*b as f64),
(a, b) => panic!("unable to compare {:?} with {:?}", a, b),
}
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Value::Int(a), Value::Int(b)) => Some(a.cmp(b)),
(Value::UInt(a), Value::UInt(b)) => Some(a.cmp(b)),
(Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
(Value::String(a), Value::String(b)) => Some(a.cmp(b)),
(Value::Bool(a), Value::Bool(b)) => Some(a.cmp(b)),
(Value::Null, Value::Null) => Some(Ordering::Equal),
(Value::Duration(a), Value::Duration(b)) => Some(a.cmp(b)),
(Value::Timestamp(a), Value::Timestamp(b)) => Some(a.cmp(b)),
(Value::Int(a), Value::UInt(b)) => Some(
a.to_owned()
.try_into()
.and_then(|a: u64| Ok(a.cmp(b)))
.unwrap_or(Ordering::Less),
),
(Value::Int(a), Value::Float(b)) => (*a as f64).partial_cmp(b),
(Value::UInt(a), Value::Int(b)) => Some(
a.to_owned()
.try_into()
.and_then(|a: i64| Ok(a.cmp(b)))
.unwrap_or(Ordering::Greater),
),
(Value::UInt(a), Value::Float(b)) => (*a as f64).partial_cmp(b),
(Value::Float(a), Value::Int(b)) => a.partial_cmp(&(*b as f64)),
(Value::Float(a), Value::UInt(b)) => a.partial_cmp(&(*b as f64)),
(a, b) => panic!("unable to compare {:?} with {:?}", a, b),
}
}
}
impl From<&Key> for Value {
fn from(value: &Key) -> Self {
match value {
Key::Int(v) => Value::Int(*v),
Key::Uint(v) => Value::UInt(*v),
Key::Bool(v) => Value::Bool(*v),
Key::String(v) => Value::String(v.clone()),
}
}
}
impl From<Key> for Value {
fn from(value: Key) -> Self {
match value {
Key::Int(v) => Value::Int(v),
Key::Uint(v) => Value::UInt(v),
Key::Bool(v) => Value::Bool(v),
Key::String(v) => Value::String(v),
}
}
}
impl From<&Key> for Key {
fn from(key: &Key) -> Self {
key.clone()
}
}
impl<T: Into<Value>> From<Vec<T>> for Value {
fn from(v: Vec<T>) -> Self {
Value::List(v.into_iter().map(|v| v.into()).collect::<Vec<_>>().into())
}
}
impl From<Vec<u8>> for Value {
fn from(v: Vec<u8>) -> Self {
Value::Bytes(v.into())
}
}
impl From<String> for Value {
fn from(v: String) -> Self {
Value::String(v.into())
}
}
impl From<&str> for Value {
fn from(v: &str) -> Self {
Value::String(v.to_string().into())
}
}
impl<T: Into<Value>> From<Option<T>> for Value {
fn from(v: Option<T>) -> Self {
match v {
Some(v) => v.into(),
None => Value::Null,
}
}
}
impl<K: Into<Key>, V: Into<Value>> From<HashMap<K, V>> for Value {
fn from(v: HashMap<K, V>) -> Self {
Value::Map(v.into())
}
}
impl From<ExecutionError> for ResolveResult {
fn from(value: ExecutionError) -> Self {
Err(value)
}
}
pub type ResolveResult = Result<Value, ExecutionError>;
impl From<Value> for ResolveResult {
fn from(value: Value) -> Self {
Ok(value)
}
}
impl<'a> Value {
pub fn resolve_all(expr: &[Expression], ctx: &Context) -> ResolveResult {
let mut res = Vec::with_capacity(expr.len());
for expr in expr {
res.push(Value::resolve(expr, ctx)?);
}
Ok(Value::List(res.into()))
}
#[inline(always)]
pub fn resolve(expr: &'a Expression, ctx: &Context) -> ResolveResult {
match expr {
Expression::Atom(atom) => Ok(atom.into()),
Expression::Arithmetic(left, op, right) => {
let left = Value::resolve(left, ctx)?;
let right = Value::resolve(right, ctx)?;
match op {
ArithmeticOp::Add => left + right,
ArithmeticOp::Subtract => left - right,
ArithmeticOp::Divide => left / right,
ArithmeticOp::Multiply => left * right,
ArithmeticOp::Modulus => left % right,
}
.into()
}
Expression::Relation(left, op, right) => {
let left = Value::resolve(left, ctx)?;
let right = Value::resolve(right, ctx)?;
let res = match op {
RelationOp::LessThan => {
left.partial_cmp(&right)
.ok_or(ExecutionError::ValuesNotComparable(left, right))?
== Ordering::Less
}
RelationOp::LessThanEq => {
left.partial_cmp(&right)
.ok_or(ExecutionError::ValuesNotComparable(left, right))?
!= Ordering::Greater
}
RelationOp::GreaterThan => {
left.partial_cmp(&right)
.ok_or(ExecutionError::ValuesNotComparable(left, right))?
== Ordering::Greater
}
RelationOp::GreaterThanEq => {
left.partial_cmp(&right)
.ok_or(ExecutionError::ValuesNotComparable(left, right))?
!= Ordering::Less
}
RelationOp::Equals => right.eq(&left),
RelationOp::NotEquals => right.ne(&left),
RelationOp::In => match (left, right) {
(Value::String(l), Value::String(r)) => r.contains(&*l),
(any, Value::List(v)) => v.contains(&any),
(any, Value::Map(m)) => m.map.contains_key(&any.try_into().unwrap()),
_ => unimplemented!(),
},
};
Value::Bool(res).into()
}
Expression::Ternary(cond, left, right) => {
let cond = Value::resolve(cond, ctx)?;
if cond.to_bool() {
Value::resolve(left, ctx)
} else {
Value::resolve(right, ctx)
}
}
Expression::Or(left, right) => {
let left = Value::resolve(left, ctx)?;
if left.to_bool() {
left.into()
} else {
Value::resolve(right, ctx)
}
}
Expression::And(left, right) => {
let left = Value::resolve(left, ctx)?;
let right = Value::resolve(right, ctx)?;
Value::Bool(left.to_bool() && right.to_bool()).into()
}
Expression::Unary(op, expr) => {
let expr = Value::resolve(expr, ctx)?;
match op {
UnaryOp::Not => Value::Bool(!expr.to_bool()),
UnaryOp::DoubleNot => Value::Bool(expr.to_bool()),
UnaryOp::Minus => match expr {
Value::Int(i) => Value::Int(-i),
Value::Float(i) => Value::Float(-i),
_ => unimplemented!(),
},
UnaryOp::DoubleMinus => match expr {
Value::Int(_) => expr,
Value::UInt(_) => expr,
Value::Float(_) => expr,
_ => unimplemented!(),
},
}
.into()
}
Expression::Member(left, right) => {
let left = Value::resolve(left, ctx)?;
left.member(right, ctx)
}
Expression::List(items) => {
let list = items
.iter()
.map(|i| Value::resolve(i, ctx))
.collect::<Result<Vec<_>, _>>()?;
Value::List(list.into()).into()
}
Expression::Map(items) => {
let mut map = HashMap::default();
for (k, v) in items.iter() {
let key = Value::resolve(k, ctx)?
.try_into()
.map_err(ExecutionError::UnsupportedKeyType)?;
let value = Value::resolve(v, ctx)?;
map.insert(key, value);
}
Value::Map(Map { map: Rc::from(map) }).into()
}
Expression::Ident(name) => {
if ctx.has_function(&***name) {
Value::Function(name.clone(), None).into()
} else {
ctx.get_variable(&***name)
}
}
}
}
fn member(self, member: &Member, ctx: &Context) -> ResolveResult {
match member {
Member::Index(idx) => {
let idx = Value::resolve(idx, ctx)?;
match (self, idx) {
(Value::List(items), Value::Int(idx)) => {
items.get(idx as usize).unwrap().clone().into()
}
(Value::String(str), Value::Int(idx)) => {
match str.get(idx as usize..(idx + 1) as usize) {
None => Value::Null,
Some(str) => Value::String(str.to_string().into()),
}
.into()
}
(Value::Map(map), Value::String(property)) => map
.get(&property.into())
.cloned()
.unwrap_or(Value::Null)
.into(),
(Value::Map(map), Value::Bool(property)) => map
.get(&property.into())
.cloned()
.unwrap_or(Value::Null)
.into(),
(Value::Map(map), Value::Int(property)) => map
.get(&property.into())
.cloned()
.unwrap_or(Value::Null)
.into(),
(Value::Map(map), Value::UInt(property)) => map
.get(&property.into())
.cloned()
.unwrap_or(Value::Null)
.into(),
_ => unimplemented!(),
}
}
Member::Fields(_) => unimplemented!(),
Member::Attribute(name) => {
let child = match self {
Value::Map(ref m) => m.map.get(&name.clone().into()).cloned(),
_ => None,
};
match (child.is_some(), ctx.has_function(&***name)) {
(false, false) => NoSuchKey(name.clone()).into(),
(true, true) | (true, false) => child.unwrap().into(),
(false, true) => Value::Function(name.clone(), Some(self.into())).into(),
}
}
Member::FunctionCall(args) => {
if let Value::Function(name, target) = self {
let func = ctx.get_function(&**name).unwrap();
match target {
None => {
let mut ctx =
FunctionContext::new(name.clone(), None, ctx, args.clone());
func.call_with_context(&mut ctx)
}
Some(target) => {
let mut ctx = FunctionContext::new(
name.clone(),
Some(*target),
ctx,
args.clone(),
);
func.call_with_context(&mut ctx)
}
}
} else {
unreachable!("FunctionCall without Value::Function - {:?}", self)
}
}
}
}
#[inline(always)]
fn to_bool(&self) -> bool {
match self {
Value::List(v) => !v.is_empty(),
Value::Map(v) => !v.map.is_empty(),
Value::Int(v) => *v != 0,
Value::UInt(v) => *v != 0,
Value::Float(v) => *v != 0.0,
Value::String(v) => !v.is_empty(),
Value::Bytes(v) => !v.is_empty(),
Value::Bool(v) => *v,
Value::Null => false,
Value::Duration(v) => v.num_nanoseconds().map(|n| n != 0).unwrap_or(false),
Value::Timestamp(v) => v.timestamp_nanos() > 0,
Value::Function(_, _) => false,
}
}
}
impl From<&Atom> for Value {
#[inline(always)]
fn from(atom: &Atom) -> Self {
match atom {
Atom::Int(v) => Value::Int(*v),
Atom::UInt(v) => Value::UInt(*v),
Atom::Float(v) => Value::Float(*v),
Atom::String(v) => Value::String(v.clone()),
Atom::Bytes(v) => Value::Bytes(v.clone()),
Atom::Bool(v) => Value::Bool(*v),
Atom::Null => Value::Null,
}
}
}
impl ops::Add<Value> for Value {
type Output = Value;
#[inline(always)]
fn add(self, rhs: Value) -> Self::Output {
match (self, rhs) {
(Value::Int(l), Value::Int(r)) => Value::Int(l + r),
(Value::UInt(l), Value::UInt(r)) => Value::UInt(l + r),
(Value::Float(l), Value::Float(r)) => Value::Float(l + r),
(Value::Int(l), Value::Float(r)) => Value::Float(l as f64 + r),
(Value::Float(l), Value::Int(r)) => Value::Float(l + r as f64),
(Value::UInt(l), Value::Float(r)) => Value::Float(l as f64 + r),
(Value::Float(l), Value::UInt(r)) => Value::Float(l + r as f64),
(Value::List(l), Value::List(r)) => {
Value::List(l.iter().chain(r.iter()).cloned().collect::<Vec<_>>().into())
}
(Value::String(l), Value::String(r)) => {
let mut new = String::with_capacity(l.len() + r.len());
new.push_str(&l);
new.push_str(&r);
Value::String(new.into())
}
(Value::Map(l), Value::Map(r)) => {
let mut new = HashMap::default();
for (k, v) in l.map.iter() {
new.insert(k.clone(), v.clone());
}
for (k, v) in r.map.iter() {
new.insert(k.clone(), v.clone());
}
Value::Map(Map { map: Rc::new(new) })
}
(Value::Duration(l), Value::Duration(r)) => Value::Duration(l + r),
(Value::Timestamp(l), Value::Duration(r)) => Value::Timestamp(l + r),
(Value::Duration(l), Value::Timestamp(r)) => Value::Timestamp(r + l),
_ => unimplemented!(),
}
}
}
impl ops::Sub<Value> for Value {
type Output = Value;
#[inline(always)]
fn sub(self, rhs: Value) -> Self::Output {
match (self, rhs) {
(Value::Int(l), Value::Int(r)) => Value::Int(l - r),
(Value::UInt(l), Value::UInt(r)) => Value::UInt(l - r),
(Value::Float(l), Value::Float(r)) => Value::Float(l - r),
(Value::Int(l), Value::Float(r)) => Value::Float(l as f64 - r),
(Value::Float(l), Value::Int(r)) => Value::Float(l - r as f64),
(Value::UInt(l), Value::Float(r)) => Value::Float(l as f64 - r),
(Value::Float(l), Value::UInt(r)) => Value::Float(l - r as f64),
(Value::Duration(l), Value::Duration(r)) => Value::Duration(l - r),
(Value::Timestamp(l), Value::Duration(r)) => Value::Timestamp(l - r),
(Value::Timestamp(l), Value::Timestamp(r)) => Value::Duration(l - r),
_ => unimplemented!(),
}
}
}
impl ops::Div<Value> for Value {
type Output = Value;
#[inline(always)]
fn div(self, rhs: Value) -> Self::Output {
match (self, rhs) {
(Value::Int(l), Value::Int(r)) => Value::Int(l / r),
(Value::UInt(l), Value::UInt(r)) => Value::UInt(l / r),
(Value::Float(l), Value::Float(r)) => Value::Float(l / r),
(Value::Int(l), Value::Float(r)) => Value::Float(l as f64 / r),
(Value::Float(l), Value::Int(r)) => Value::Float(l / r as f64),
(Value::UInt(l), Value::Float(r)) => Value::Float(l as f64 / r),
(Value::Float(l), Value::UInt(r)) => Value::Float(l / r as f64),
_ => unimplemented!(),
}
}
}
impl ops::Mul<Value> for Value {
type Output = Value;
#[inline(always)]
fn mul(self, rhs: Value) -> Self::Output {
match (self, rhs) {
(Value::Int(l), Value::Int(r)) => Value::Int(l * r),
(Value::UInt(l), Value::UInt(r)) => Value::UInt(l * r),
(Value::Float(l), Value::Float(r)) => Value::Float(l * r),
(Value::Int(l), Value::Float(r)) => Value::Float(l as f64 * r),
(Value::Float(l), Value::Int(r)) => Value::Float(l * r as f64),
(Value::UInt(l), Value::Float(r)) => Value::Float(l as f64 * r),
(Value::Float(l), Value::UInt(r)) => Value::Float(l * r as f64),
_ => unimplemented!(),
}
}
}
impl ops::Rem<Value> for Value {
type Output = Value;
#[inline(always)]
fn rem(self, rhs: Value) -> Self::Output {
match (self, rhs) {
(Value::Int(l), Value::Int(r)) => Value::Int(l % r),
(Value::UInt(l), Value::UInt(r)) => Value::UInt(l % r),
(Value::Float(l), Value::Float(r)) => Value::Float(l % r),
(Value::Int(l), Value::Float(r)) => Value::Float(l as f64 % r),
(Value::Float(l), Value::Int(r)) => Value::Float(l % r as f64),
(Value::UInt(l), Value::Float(r)) => Value::Float(l as f64 % r),
(Value::Float(l), Value::UInt(r)) => Value::Float(l % r as f64),
_ => unimplemented!(),
}
}
}
#[cfg(test)]
mod tests {
use crate::{objects::Key, Context, Program};
use std::collections::HashMap;
#[test]
fn test_indexed_map_access() {
let mut context = Context::default();
let mut headers = HashMap::new();
headers.insert("Content-Type", "application/json".to_string());
context.add_variable_from_value("headers", headers);
let program = Program::compile("headers[\"Content-Type\"]").unwrap();
let value = program.execute(&context).unwrap();
assert_eq!(value, "application/json".into());
}
#[test]
fn test_numeric_map_access() {
let mut context = Context::default();
let mut numbers = HashMap::new();
numbers.insert(Key::Uint(1), "one".to_string());
context.add_variable_from_value("numbers", numbers);
let program = Program::compile("numbers[1]").unwrap();
let value = program.execute(&context).unwrap();
assert_eq!(value, "one".into());
}
#[test]
fn test_heterogeneous_compare() {
let context = Context::default();
let program = Program::compile("1 < uint(2)").unwrap();
let value = program.execute(&context).unwrap();
assert_eq!(value, true.into());
let program = Program::compile("1 < 1.1").unwrap();
let value = program.execute(&context).unwrap();
assert_eq!(value, true.into());
let program = Program::compile("uint(0) > -10").unwrap();
let value = program.execute(&context).unwrap();
assert_eq!(
value,
true.into(),
"negative signed ints should be less than uints"
);
}
#[test]
fn test_float_compare() {
let context = Context::default();
let program = Program::compile("1.0 > 0.0").unwrap();
let value = program.execute(&context).unwrap();
assert_eq!(value, true.into());
let program = Program::compile("double('NaN') == double('NaN')").unwrap();
let value = program.execute(&context).unwrap();
assert_eq!(value, false.into(), "NaN should not equal itself");
let program = Program::compile("1.0 > double('NaN')").unwrap();
let result = program.execute(&context);
assert!(
result.is_err(),
"NaN should not be comparable with inequality operators"
);
}
}