diff --git a/VAR.ExpressionEvaluator.Tests/ParserTests.cs b/VAR.ExpressionEvaluator.Tests/ParserTests.cs index d5ced32..d8574b1 100644 --- a/VAR.ExpressionEvaluator.Tests/ParserTests.cs +++ b/VAR.ExpressionEvaluator.Tests/ParserTests.cs @@ -547,6 +547,11 @@ namespace VAR.ExpressionEvaluator.Tests Assert.False((bool?)Parser.EvaluateString("true and false")); Assert.True((bool?)Parser.EvaluateString("true and true")); + Assert.False((bool?)Parser.EvaluateString("False And False")); + Assert.False((bool?)Parser.EvaluateString("False And True")); + Assert.False((bool?)Parser.EvaluateString("True And False")); + Assert.True((bool?)Parser.EvaluateString("True And True")); + Assert.False((bool?)Parser.EvaluateString("false && false")); Assert.False((bool?)Parser.EvaluateString("false && true")); Assert.False((bool?)Parser.EvaluateString("true && false")); @@ -561,6 +566,11 @@ namespace VAR.ExpressionEvaluator.Tests Assert.True((bool?)Parser.EvaluateString("true or false")); Assert.True((bool?)Parser.EvaluateString("true or true")); + Assert.False((bool?)Parser.EvaluateString("False Or False")); + Assert.True((bool?)Parser.EvaluateString("False Or True")); + Assert.True((bool?)Parser.EvaluateString("True Or False")); + Assert.True((bool?)Parser.EvaluateString("True Or True")); + Assert.False((bool?)Parser.EvaluateString("false || false")); Assert.True((bool?)Parser.EvaluateString("false || true")); Assert.True((bool?)Parser.EvaluateString("true || false")); @@ -577,7 +587,6 @@ namespace VAR.ExpressionEvaluator.Tests Assert.False((bool?)Parser.EvaluateString("not true")); } - #endregion BooleanOps #region Null value @@ -621,6 +630,40 @@ namespace VAR.ExpressionEvaluator.Tests #endregion + #region String coercions + + [Fact()] + public void StringCoercions__Var1NullEqualsStringEmpty_EqualsTrue() + { + EvaluationContext evaluationContex = new EvaluationContext(); + evaluationContex.SetVariable("v1", null); + string expression = "v1 = \"\""; + object result = Parser.EvaluateString(expression, evaluationContex); + Assert.True((bool?)result); + } + + [Fact()] + public void StringCoercions__Var1TrueStringEqualsTrue_EqualsTrue() + { + EvaluationContext evaluationContex = new EvaluationContext(); + evaluationContex.SetVariable("v1", "True"); + string expression = "v1 = true"; + object result = Parser.EvaluateString(expression, evaluationContex); + Assert.True((bool?)result); + } + + [Fact()] + public void StringCoercions__Var1FalseStringEqualsFalse_EqualsTrue() + { + EvaluationContext evaluationContex = new EvaluationContext(); + evaluationContex.SetVariable("v1", "False"); + string expression = "v1 = false"; + object result = Parser.EvaluateString(expression, evaluationContex); + Assert.True((bool?)result); + } + + #endregion String coercions + #region Excepctions [Fact] diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs index 7111c96..f27c703 100644 --- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs @@ -1,6 +1,4 @@ -using System; - -namespace VAR.ExpressionEvaluator +namespace VAR.ExpressionEvaluator { public class ExpressionEqualsNode : ExpressionBinaryNode { @@ -9,49 +7,17 @@ namespace VAR.ExpressionEvaluator { } - private static object EqualsOp(object leftValue, object rightValue) + public static object EqualsOp(object leftValue, object rightValue) { - if (leftValue is string && rightValue is string) + // Null + if (leftValue is string && string.IsNullOrEmpty(leftValue as string)) { - return string.Compare((string)leftValue, (string)rightValue) == 0; + leftValue = null; } - - if ((leftValue is string && rightValue == null) || (leftValue == null && rightValue is string)) + if (rightValue is string && string.IsNullOrEmpty(rightValue as string)) { - return false; + rightValue = null; } - - if (leftValue is string) - { - if (string.IsNullOrEmpty((string)leftValue)) - { - leftValue = null; - } - else - { - if (decimal.TryParse((string)leftValue, out decimal dec) == false) - { - throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue)); - } - leftValue = dec; - } - } - if (rightValue is string) - { - if (string.IsNullOrEmpty((string)rightValue)) - { - leftValue = null; - } - else - { - if (decimal.TryParse((string)rightValue, out decimal dec) == false) - { - throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue)); - } - rightValue = dec; - } - } - if (leftValue == null && rightValue == null) { return true; @@ -61,11 +27,85 @@ namespace VAR.ExpressionEvaluator return false; } + // String + if (leftValue is string && rightValue is string) + { + return string.Compare((string)leftValue, (string)rightValue) == 0; + } + + // Bool + if (leftValue is bool || rightValue is bool) + { + bool? leftBool = ConvertToNullableBool(leftValue); + bool? rightBool = ConvertToNullableBool(rightValue); + return leftBool == rightBool; + } + + // Decimal + if (leftValue is string) + { + if (decimal.TryParse((string)leftValue, out decimal dec) == false) + { + leftValue = null; + } + else + { + leftValue = dec; + } + } + if (rightValue is string) + { + if (decimal.TryParse((string)rightValue, out decimal dec) == false) + { + rightValue = null; + } + else + { + rightValue = dec; + } + } if ((leftValue is decimal) == false || (rightValue is decimal) == false) { - throw new Exception("Can't compare non decimal values"); + return false; } return (decimal)leftValue == (decimal)rightValue; } + + private static bool? ConvertToNullableBool(object value) + { + if (value is bool) + { + return (bool)value; + } + if (value is string) + { + string text = value as string; + if (string.IsNullOrEmpty(text)) + { + return null; + } + decimal decValue; + if (decimal.TryParse(text, out decValue)) + { + return decValue != 0; + } + string textLower = text.ToLower(); + if (textLower == "false" || textLower == "no" || textLower == "ez" || textLower == "non" || textLower == "nein") + { + return false; + } + if (textLower == "true" || textLower == "si" || textLower == "sí" || textLower == "yes" || textLower == "bai" || textLower == "oui" || textLower == "ja") + { + return true; + } + + return null; + } + if (value is decimal) + { + return ((decimal)value) != 0; + } + return false; + } } } diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs index fb5ac48..3fa997f 100644 --- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs @@ -1,6 +1,4 @@ -using System; - -namespace VAR.ExpressionEvaluator +namespace VAR.ExpressionEvaluator { public class ExpressionNotEqualsNode : ExpressionBinaryNode { @@ -11,56 +9,8 @@ namespace VAR.ExpressionEvaluator private static object NotEqualsOp(object leftValue, object rightValue) { - if (leftValue is string && rightValue is string) - { - return string.Compare((string)leftValue, (string)rightValue) != 0; - } - - if (leftValue is string) - { - if (string.IsNullOrEmpty((string)leftValue)) - { - leftValue = null; - } - else - { - if (decimal.TryParse((string)leftValue, out decimal dec) == false) - { - throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue)); - } - leftValue = dec; - } - } - if (rightValue is string) - { - if (string.IsNullOrEmpty((string)rightValue)) - { - leftValue = null; - } - else - { - if (decimal.TryParse((string)rightValue, out decimal dec) == false) - { - throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue)); - } - rightValue = dec; - } - } - - if (leftValue == null && rightValue == null) - { - return false; - } - if (leftValue == null || rightValue == null) - { - return true; - } - - if ((leftValue is decimal) == false || (rightValue is decimal) == false) - { - throw new Exception("Can't compare non decimal values"); - } - return (decimal)leftValue != (decimal)rightValue; + bool result = (bool)ExpressionEqualsNode.EqualsOp(leftValue, rightValue); + return !result; } } } diff --git a/VAR.ExpressionEvaluator/Parser.cs b/VAR.ExpressionEvaluator/Parser.cs index 6934842..81be887 100644 --- a/VAR.ExpressionEvaluator/Parser.cs +++ b/VAR.ExpressionEvaluator/Parser.cs @@ -84,6 +84,13 @@ namespace VAR.ExpressionEvaluator IExpressionNode rightNode = ParsePlusAndMinus(); leftNode = new ExpressionEqualsNode(leftNode, rightNode); } + if (_tokenizer.Token == Token.ExclusiveEquals) + { + // TODO: Implement ExpressionExclusiveEqualsNode + _tokenizer.NextToken(); + IExpressionNode rightNode = ParsePlusAndMinus(); + leftNode = new ExpressionEqualsNode(leftNode, rightNode); + } else if (_tokenizer.Token == Token.NotEquals) { _tokenizer.NextToken();