private FluidValue Evaluate(FluidValue leftValue, FluidValue rightValue)
        {
            // If the values are of the same type, use the default comparison.
            if (leftValue.Type == rightValue.Type)
            {
                if (FailIfEqual)
                {
                    return(leftValue.Equals(rightValue) ? BooleanValue.False : BooleanValue.True);
                }
                else
                {
                    return(leftValue.Equals(rightValue) ? BooleanValue.True : BooleanValue.False);
                }
            }

            // If either value is boolean, perform a boolean comparison.
            if (leftValue is BooleanValue || rightValue is BooleanValue)
            {
                // If comparing with an empty string, any value will satisfy the predicate.
                if (leftValue.Type == FluidValues.String && leftValue.ToStringValue() == string.Empty)
                {
                    return(FailIfEqual ? BooleanValue.True : BooleanValue.False);
                }
                else if (rightValue.Type == FluidValues.String && rightValue.ToStringValue() == string.Empty)
                {
                    return(FailIfEqual ? BooleanValue.True : BooleanValue.False);
                }

                var leftBoolean  = leftValue.ToBooleanValue();
                var rightBoolean = rightValue.ToBooleanValue();

                if (FailIfEqual)
                {
                    return(leftBoolean == rightBoolean ? BooleanValue.False : BooleanValue.True);
                }
                else
                {
                    return(leftBoolean == rightBoolean ? BooleanValue.True : BooleanValue.False);
                }
            }

            // If either value is an Enum, perform an Enum comparison.
            if (leftValue is LavaEnumValue lv)
            {
                return(EnumValueIsEqualToOtherTypeValue(lv, rightValue));
            }
            if (rightValue is LavaEnumValue rv)
            {
                return(EnumValueIsEqualToOtherTypeValue(rv, leftValue));
            }

            // If either value is numeric, and both values can be converted to a number, perform a numeric comparison.
            if (leftValue is NumberValue || rightValue is NumberValue)
            {
                decimal leftDecimal;
                decimal rightDecimal;

                if (decimal.TryParse(leftValue.ToStringValue(), out leftDecimal) &&
                    decimal.TryParse(rightValue.ToStringValue(), out rightDecimal))
                {
                    if (FailIfEqual)
                    {
                        return(leftDecimal == rightDecimal ? BooleanValue.False : BooleanValue.True);
                    }
                    else
                    {
                        return(leftDecimal == rightDecimal ? BooleanValue.True : BooleanValue.False);
                    }
                }
            }

            // If either value is a Date, perform a Date comparison.
            if (leftValue.Type == FluidValues.DateTime || rightValue.Type == FluidValues.DateTime)
            {
                var leftDateTime  = leftValue.AsDateTimeOrDefault();
                var rightDateTime = rightValue.AsDateTimeOrDefault();

                if (FailIfEqual)
                {
                    return(leftDateTime == rightDateTime ? BooleanValue.False : BooleanValue.True);
                }
                else
                {
                    return(leftDateTime == rightDateTime ? BooleanValue.True : BooleanValue.False);
                }
            }

            // Use the default comparison.
            if (FailIfEqual)
            {
                return(leftValue.Equals(rightValue) ? BooleanValue.False : BooleanValue.True);
            }
            else
            {
                return(leftValue.Equals(rightValue) ? BooleanValue.True : BooleanValue.False);
            }
        }
        private FluidValue Evaluate(FluidValue leftValue, FluidValue rightValue)
        {
            if (leftValue.IsNil() || rightValue.IsNil())
            {
                if (FailIfEqual)
                {
                    return(BooleanValue.False);
                }

                return(leftValue.IsNil() && rightValue.IsNil()
                    ? BooleanValue.True
                    : BooleanValue.False);
            }

            // If either value is numeric, perform a numeric comparison.
            if (leftValue is NumberValue || rightValue is NumberValue)
            {
                var leftDecimal  = leftValue.ToNumberValue();
                var rightDecimal = rightValue.ToNumberValue();

                if (FailIfEqual)
                {
                    return(leftDecimal > rightDecimal
                        ? BooleanValue.True
                        : BooleanValue.False);
                }

                return(leftDecimal >= rightDecimal
                    ? BooleanValue.True
                    : BooleanValue.False);
            }

            // If either value is a Date, perform a Date comparison.
            if (leftValue.Type == FluidValues.DateTime || rightValue.Type == FluidValues.DateTime)
            {
                var leftDateTime  = leftValue.AsDateTimeOrDefault();
                var rightDateTime = rightValue.AsDateTimeOrDefault();

                if (FailIfEqual)
                {
                    return(leftDateTime > rightDateTime
                        ? BooleanValue.True
                        : BooleanValue.False);
                }

                return(leftDateTime >= rightDateTime
                    ? BooleanValue.True
                    : BooleanValue.False);
            }

            // If either value supports IComparable, attempt to use a built-in comparison.
            var leftObject  = leftValue.ToRealObjectValue();
            var rightObject = rightValue.ToRealObjectValue();

            if (leftObject is IComparable leftComparable)
            {
                if (FailIfEqual)
                {
                    return(leftComparable.CompareTo(rightObject) > 0 ? BooleanValue.True : BooleanValue.False);
                }
                else
                {
                    return(leftComparable.CompareTo(rightObject) >= 0 ? BooleanValue.True : BooleanValue.False);
                }
            }

            if (rightObject is IComparable rightComparable)
            {
                if (FailIfEqual)
                {
                    return(rightComparable.CompareTo(leftObject) > 0 ? BooleanValue.True : BooleanValue.False);
                }
                else
                {
                    return(rightComparable.CompareTo(leftObject) >= 0 ? BooleanValue.True : BooleanValue.False);
                }
            }

            // The values cannot be compared in a meaningful way.
            return(NilValue.Instance);
        }