diff --git a/VAR.ExpressionEvaluator.Tests/ParserTests.cs b/VAR.ExpressionEvaluator.Tests/ParserTests.cs
index 0690896..75847fb 100644
--- a/VAR.ExpressionEvaluator.Tests/ParserTests.cs
+++ b/VAR.ExpressionEvaluator.Tests/ParserTests.cs
@@ -401,5 +401,45 @@ namespace VAR.ExpressionEvaluator.Tests
#endregion BooleanOps
+ #region Null value
+
+
+ [TestMethod()]
+ public void NullValue_NullPlusAnything_EqualsNull()
+ {
+ Assert.AreEqual(null, Parser.EvaluateString("null + 1"));
+ Assert.AreEqual(null, Parser.EvaluateString("null + 100"));
+ Assert.AreEqual(null, Parser.EvaluateString("1 + null"));
+ Assert.AreEqual(null, Parser.EvaluateString("100 + null"));
+ Assert.AreEqual(null, Parser.EvaluateString("null + null"));
+ }
+
+ [TestMethod()]
+ public void NullValue_NullMinusAnything_EqualsNull()
+ {
+ Assert.AreEqual(null, Parser.EvaluateString("null - 1"));
+ Assert.AreEqual(null, Parser.EvaluateString("null - 100"));
+ Assert.AreEqual(null, Parser.EvaluateString("1 - null"));
+ Assert.AreEqual(null, Parser.EvaluateString("100 - null"));
+ Assert.AreEqual(null, Parser.EvaluateString("null - null"));
+ }
+
+ [TestMethod()]
+ public void NullValue_NullByAnything_EqualsNull()
+ {
+ Assert.AreEqual(null, Parser.EvaluateString("null * 1"));
+ Assert.AreEqual(null, Parser.EvaluateString("null * 100"));
+ Assert.AreEqual(null, Parser.EvaluateString("1 * null"));
+ Assert.AreEqual(null, Parser.EvaluateString("100 * null"));
+ Assert.AreEqual(null, Parser.EvaluateString("null * null"));
+
+ Assert.AreEqual(null, Parser.EvaluateString("null / 1"));
+ Assert.AreEqual(null, Parser.EvaluateString("null / 100"));
+ Assert.AreEqual(null, Parser.EvaluateString("1 / null"));
+ Assert.AreEqual(null, Parser.EvaluateString("100 / null"));
+ Assert.AreEqual(null, Parser.EvaluateString("null / null"));
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/VAR.ExpressionEvaluator/EvaluationContext.cs b/VAR.ExpressionEvaluator/EvaluationContext.cs
index 3ba47ca..b092d82 100644
--- a/VAR.ExpressionEvaluator/EvaluationContext.cs
+++ b/VAR.ExpressionEvaluator/EvaluationContext.cs
@@ -39,6 +39,11 @@ namespace VAR.ExpressionEvaluator
public void SetVariable(string name, object value)
{
+ if (value is DBNull)
+ {
+ value = null;
+ }
+
if (value is DateTime)
{
value = ((DateTime)value).ToString("s");
@@ -60,7 +65,7 @@ namespace VAR.ExpressionEvaluator
{
if (_variables.ContainsKey(name) == false)
{
- return null;
+ throw new Exception(string.Format("Variable {0} not found", name));
}
return _variables[name];
}
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanAndNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanAndNode.cs
index dca5046..24fa304 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanAndNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanAndNode.cs
@@ -9,9 +9,10 @@
private static object BooleanAndOp(object leftValue, object rightValue)
{
- bool bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue);
- bool brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue);
- return bLeftValue && brightValue;
+ bool? bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue);
+ bool? brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue);
+ if (bLeftValue == null || bLeftValue == null) { return null; }
+ return bLeftValue.Value && brightValue.Value;
}
}
}
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNode.cs
index cc4efae..3f3e042 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNode.cs
@@ -14,8 +14,13 @@
return _value;
}
- public static bool ConvertToBoolean(object value)
+ public static bool? ConvertToBoolean(object value)
{
+ if (value == null)
+ {
+ return null;
+ }
+
if (value is bool)
{
return (bool)value;
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNotNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNotNode.cs
index ebb06cb..81b5436 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNotNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNotNode.cs
@@ -10,6 +10,7 @@
private static object BooleanNotOp(object value)
{
value = ExpressionBooleanNode.ConvertToBoolean(value);
+ if (value == null) { return null; }
return !(bool)value;
}
}
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanOrNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanOrNode.cs
index d0127db..2a0682b 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanOrNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanOrNode.cs
@@ -9,9 +9,10 @@
private static object BooleanOrOp(object leftValue, object rightValue)
{
- bool bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue);
- bool brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue);
- return bLeftValue || brightValue;
+ bool? bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue);
+ bool? brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue);
+ if (bLeftValue == null || bLeftValue == null) { return null; }
+ return bLeftValue.Value || brightValue.Value;
}
}
}
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionDivisionNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionDivisionNode.cs
index 5385222..62c04a3 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionDivisionNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionDivisionNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object DivisionOp(object leftValue, object rightValue)
{
+ if (leftValue == null || rightValue == null)
+ {
+ return null;
+ }
+
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs
index 3ed90a7..20d0c99 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs
@@ -11,6 +11,15 @@ namespace VAR.ExpressionEvaluator
private static object EqualsOp(object leftValue, object rightValue)
{
+ if (leftValue == null && rightValue == null)
+ {
+ return true;
+ }
+ if (leftValue == null || rightValue == null)
+ {
+ return false;
+ }
+
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) == 0;
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterOrEqualThanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterOrEqualThanNode.cs
index 7fd26dc..c6f88d2 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterOrEqualThanNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterOrEqualThanNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object GreaterOrEqualThanOp(object leftValue, object rightValue)
{
+ if (leftValue == null || rightValue == null)
+ {
+ return false;
+ }
+
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) >= 0;
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterThanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterThanNode.cs
index d7a0fb9..2029c58 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterThanNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterThanNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object GreaterThanOp(object leftValue, object rightValue)
{
+ if (leftValue == null || rightValue == null)
+ {
+ return false;
+ }
+
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) > 0;
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessOrEqualThanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessOrEqualThanNode.cs
index 8028d70..b644ad1 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessOrEqualThanNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessOrEqualThanNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object LessOrEqualThanOp(object leftValue, object rightValue)
{
+ if (leftValue == null || rightValue == null)
+ {
+ return false;
+ }
+
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) <= 0;
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessThanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessThanNode.cs
index 6d38cbb..9e42daf 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessThanNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessThanNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object LessThanOp(object leftValue, object rightValue)
{
+ if (leftValue == null || rightValue == null)
+ {
+ return false;
+ }
+
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) < 0;
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionMinusNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionMinusNode.cs
index 76ef5c1..9264bcf 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionMinusNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionMinusNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object MinusOp(object leftValue, object rightValue)
{
+ if (leftValue == null || rightValue == null)
+ {
+ return null;
+ }
+
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionMultiplyNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionMultiplyNode.cs
index e581840..b9dbb1f 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionMultiplyNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionMultiplyNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object MultiplyOp(object leftValue, object rightValue)
{
+ if (leftValue == null || rightValue == null)
+ {
+ return null;
+ }
+
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs
index e1f5b88..0428e12 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs
@@ -11,6 +11,15 @@ namespace VAR.ExpressionEvaluator
private static object NotEqualsOp(object leftValue, object rightValue)
{
+ if (leftValue == null && rightValue == null)
+ {
+ return false;
+ }
+ if (leftValue == null || rightValue == null)
+ {
+ return true;
+ }
+
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) != 0;
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNullNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNullNode.cs
new file mode 100644
index 0000000..288cc24
--- /dev/null
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNullNode.cs
@@ -0,0 +1,10 @@
+namespace VAR.ExpressionEvaluator
+{
+ public class ExpressionNullNode : IExpressionNode
+ {
+ public object Eval(IEvaluationContext evaluationContext)
+ {
+ return null;
+ }
+ }
+}
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNumberNegateNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNumberNegateNode.cs
index b76f5eb..cd70aba 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNumberNegateNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNumberNegateNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object NumberNegateOp(object value)
{
+ if (value == null)
+ {
+ return null;
+ }
+
if (value is string)
{
if (decimal.TryParse((string)value, out decimal dec) == false)
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionPlusNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionPlusNode.cs
index 91430cc..991bb2a 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionPlusNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionPlusNode.cs
@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object PlusOp(object leftValue, object rightValue)
{
+ if (leftValue == null || rightValue == null)
+ {
+ return null;
+ }
+
if (leftValue is string || rightValue is string)
{
return string.Concat(Convert.ToString(leftValue), Convert.ToString(rightValue));
diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionVariableNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionVariableNode.cs
index 0bb209a..36a6fbf 100644
--- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionVariableNode.cs
+++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionVariableNode.cs
@@ -14,10 +14,6 @@ namespace VAR.ExpressionEvaluator
public object Eval(IEvaluationContext evaluationContext)
{
object value = evaluationContext.GetVariable(_name);
- if (value == null)
- {
- throw new Exception(string.Format("Variable {0} not found", _name));
- }
return value;
}
}
diff --git a/VAR.ExpressionEvaluator/Parser.cs b/VAR.ExpressionEvaluator/Parser.cs
index 9d9fd31..d6da62e 100644
--- a/VAR.ExpressionEvaluator/Parser.cs
+++ b/VAR.ExpressionEvaluator/Parser.cs
@@ -197,6 +197,11 @@ namespace VAR.ExpressionEvaluator
_tokenizer.NextToken();
return new ExpressionBooleanNode(false);
}
+ if (identifierToLower == "null")
+ {
+ _tokenizer.NextToken();
+ return new ExpressionNullNode();
+ }
string identifier = _tokenizer.Text;
_tokenizer.NextToken();
diff --git a/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj b/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj
index f19bedd..51059c2 100644
--- a/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj
+++ b/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj
@@ -55,6 +55,7 @@
+