diff --git a/VAR.ExpressionEvaluator.Tests/ParserTests.cs b/VAR.ExpressionEvaluator.Tests/ParserTests.cs index d4d9000..19e70c5 100644 --- a/VAR.ExpressionEvaluator.Tests/ParserTests.cs +++ b/VAR.ExpressionEvaluator.Tests/ParserTests.cs @@ -256,5 +256,73 @@ namespace VAR.ExpressionEvaluator.Tests #endregion Strings + #region Relations + + [TestMethod()] + public void Relations_1GreatherThan2_EqualsFalse() + { + string expression = "1>2"; + object result = Parser.EvaluateString(expression); + Assert.AreEqual(false, result); + } + + [TestMethod()] + public void Relations_1Equals1_EqualsTrue() + { + string expression = "1=1"; + object result = Parser.EvaluateString(expression); + Assert.AreEqual(true, result); + } + + [TestMethod()] + public void Relations_10NotEquals1_EqualsTrue() + { + string expression = "10!=1"; + object result = Parser.EvaluateString(expression); + Assert.AreEqual(true, result); + } + + [TestMethod()] + public void Relations_10LessThan1_EqualsFalse() + { + string expression = "10<1"; + object result = Parser.EvaluateString(expression); + Assert.AreEqual(false, result); + } + + [TestMethod()] + public void Relations_1GreaterOrEqualThan1_EqualsTrue() + { + string expression = "1>=1"; + object result = Parser.EvaluateString(expression); + Assert.AreEqual(true, result); + } + + [TestMethod()] + public void Relations_1LessOrEqualThan1_EqualsTrue() + { + string expression = "1<=1"; + object result = Parser.EvaluateString(expression); + Assert.AreEqual(true, result); + } + + [TestMethod()] + public void Relations_1GreaterOrEqualThan10_EqualsFalse() + { + string expression = "1>=10"; + object result = Parser.EvaluateString(expression); + Assert.AreEqual(false, result); + } + + [TestMethod()] + public void Relations_10LessOrEqualThan1_EqualsFalse() + { + string expression = "10<=1"; + object result = Parser.EvaluateString(expression); + Assert.AreEqual(false, result); + } + + #endregion Relations + } } \ No newline at end of file diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs new file mode 100644 index 0000000..3ed90a7 --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionEqualsNode.cs @@ -0,0 +1,43 @@ +using System; + +namespace VAR.ExpressionEvaluator +{ + public class ExpressionEqualsNode : ExpressionBinaryNode + { + public ExpressionEqualsNode(IExpressionNode leftNode, IExpressionNode rightNode) : + base(leftNode, rightNode, EqualsOp) + { + } + + private static object EqualsOp(object leftValue, object rightValue) + { + if (leftValue is string && rightValue is string) + { + return string.Compare((string)leftValue, (string)rightValue) == 0; + } + + if (leftValue is string) + { + 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 (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 is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't compare non decimal values"); + } + return (decimal)leftValue == (decimal)rightValue; + } + } +} diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterOrEqualThanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterOrEqualThanNode.cs new file mode 100644 index 0000000..7fd26dc --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterOrEqualThanNode.cs @@ -0,0 +1,43 @@ +using System; + +namespace VAR.ExpressionEvaluator +{ + public class ExpressionGreaterOrEqualThanNode : ExpressionBinaryNode + { + public ExpressionGreaterOrEqualThanNode(IExpressionNode leftNode, IExpressionNode rightNode) : + base(leftNode, rightNode, GreaterOrEqualThanOp) + { + } + + private static object GreaterOrEqualThanOp(object leftValue, object rightValue) + { + if (leftValue is string && rightValue is string) + { + return string.Compare((string)leftValue, (string)rightValue) >= 0; + } + + if (leftValue is string) + { + 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 (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 is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't compare non decimal values"); + } + return (decimal)leftValue >= (decimal)rightValue; + } + } +} diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterThanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterThanNode.cs new file mode 100644 index 0000000..d7a0fb9 --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionGreaterThanNode.cs @@ -0,0 +1,43 @@ +using System; + +namespace VAR.ExpressionEvaluator +{ + public class ExpressionGreaterThanNode : ExpressionBinaryNode + { + public ExpressionGreaterThanNode(IExpressionNode leftNode, IExpressionNode rightNode) : + base(leftNode, rightNode, GreaterThanOp) + { + } + + private static object GreaterThanOp(object leftValue, object rightValue) + { + if (leftValue is string && rightValue is string) + { + return string.Compare((string)leftValue, (string)rightValue) > 0; + } + + if (leftValue is string) + { + 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 (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 is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't compare non decimal values"); + } + return (decimal)leftValue > (decimal)rightValue; + } + } +} diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessOrEqualThanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessOrEqualThanNode.cs new file mode 100644 index 0000000..8028d70 --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessOrEqualThanNode.cs @@ -0,0 +1,43 @@ +using System; + +namespace VAR.ExpressionEvaluator +{ + public class ExpressionLessOrEqualThanNode : ExpressionBinaryNode + { + public ExpressionLessOrEqualThanNode(IExpressionNode leftNode, IExpressionNode rightNode) : + base(leftNode, rightNode, LessOrEqualThanOp) + { + } + + private static object LessOrEqualThanOp(object leftValue, object rightValue) + { + if (leftValue is string && rightValue is string) + { + return string.Compare((string)leftValue, (string)rightValue) <= 0; + } + + if (leftValue is string) + { + 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 (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 is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't substract non decimal values"); + } + return (decimal)leftValue <= (decimal)rightValue; + } + } +} diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessThanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessThanNode.cs new file mode 100644 index 0000000..6d38cbb --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionLessThanNode.cs @@ -0,0 +1,43 @@ +using System; + +namespace VAR.ExpressionEvaluator +{ + public class ExpressionLessThanNode : ExpressionBinaryNode + { + public ExpressionLessThanNode(IExpressionNode leftNode, IExpressionNode rightNode) : + base(leftNode, rightNode, LessThanOp) + { + } + + private static object LessThanOp(object leftValue, object rightValue) + { + if (leftValue is string && rightValue is string) + { + return string.Compare((string)leftValue, (string)rightValue) < 0; + } + + if (leftValue is string) + { + 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 (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 is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't compare non decimal values"); + } + return (decimal)leftValue < (decimal)rightValue; + } + } +} diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs new file mode 100644 index 0000000..e1f5b88 --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionNotEqualsNode.cs @@ -0,0 +1,43 @@ +using System; + +namespace VAR.ExpressionEvaluator +{ + public class ExpressionNotEqualsNode : ExpressionBinaryNode + { + public ExpressionNotEqualsNode(IExpressionNode leftNode, IExpressionNode rightNode) : + base(leftNode, rightNode, NotEqualsOp) + { + } + + 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 (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 (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 is decimal) == false || (rightValue is decimal) == false) + { + throw new Exception("Can't compare non decimal values"); + } + return (decimal)leftValue != (decimal)rightValue; + } + } +} diff --git a/VAR.ExpressionEvaluator/Parser.cs b/VAR.ExpressionEvaluator/Parser.cs index d764be0..9a489b3 100644 --- a/VAR.ExpressionEvaluator/Parser.cs +++ b/VAR.ExpressionEvaluator/Parser.cs @@ -15,7 +15,7 @@ namespace VAR.ExpressionEvaluator public IExpressionNode ParseExpression() { - var expr = ParsePlusAndMinus(); + var expr = ParseRelations(); if (_tokenizer.Token != Token.EOF) { @@ -25,7 +25,55 @@ namespace VAR.ExpressionEvaluator return expr; } - public IExpressionNode ParsePlusAndMinus() + private IExpressionNode ParseRelations() + { + IExpressionNode leftNode = ParsePlusAndMinus(); + while (true) + { + if (_tokenizer.Token == Token.Equals || _tokenizer.Token == Token.ExclusiveEquals) + { + _tokenizer.NextToken(); + IExpressionNode rightNode = ParsePlusAndMinus(); + leftNode = new ExpressionEqualsNode(leftNode, rightNode); + } + else if (_tokenizer.Token == Token.NotEquals) + { + _tokenizer.NextToken(); + IExpressionNode rightNode = ParsePlusAndMinus(); + leftNode = new ExpressionNotEqualsNode(leftNode, rightNode); + } + else if (_tokenizer.Token == Token.LessThan) + { + _tokenizer.NextToken(); + IExpressionNode rightNode = ParsePlusAndMinus(); + leftNode = new ExpressionLessThanNode(leftNode, rightNode); + } + else if (_tokenizer.Token == Token.LessOrEqualThan) + { + _tokenizer.NextToken(); + IExpressionNode rightNode = ParsePlusAndMinus(); + leftNode = new ExpressionLessOrEqualThanNode(leftNode, rightNode); + } + else if (_tokenizer.Token == Token.GreaterThan) + { + _tokenizer.NextToken(); + IExpressionNode rightNode = ParsePlusAndMinus(); + leftNode = new ExpressionGreaterThanNode(leftNode, rightNode); + } + else if (_tokenizer.Token == Token.GreaterOrEqualThan) + { + _tokenizer.NextToken(); + IExpressionNode rightNode = ParsePlusAndMinus(); + leftNode = new ExpressionGreaterOrEqualThanNode(leftNode, rightNode); + } + else + { + return leftNode; + } + } + } + + private IExpressionNode ParsePlusAndMinus() { IExpressionNode leftNode = ParseMultiplyDivision(); while (true) @@ -155,7 +203,7 @@ namespace VAR.ExpressionEvaluator if (_tokenizer.Token == Token.ParenthesisStart) { _tokenizer.NextToken(); - IExpressionNode node = ParsePlusAndMinus(); + IExpressionNode node = ParseRelations(); if (_tokenizer.Token != Token.ParenthesisEnd) { throw new Exception("Missing close parenthesis"); diff --git a/VAR.ExpressionEvaluator/Token.cs b/VAR.ExpressionEvaluator/Token.cs index 8e51910..3d39201 100644 --- a/VAR.ExpressionEvaluator/Token.cs +++ b/VAR.ExpressionEvaluator/Token.cs @@ -7,14 +7,16 @@ Minus, Division, Multiply, + ParenthesisStart, + ParenthesisEnd, + Not, + NotEquals, Equals, ExclusiveEquals, GreaterThan, GreaterOrEqualThan, LessThan, LessOrEqualThan, - ParenthesisStart, - ParenthesisEnd, Comma, Identifier, String, diff --git a/VAR.ExpressionEvaluator/Tokenizer.cs b/VAR.ExpressionEvaluator/Tokenizer.cs index a2506b1..ac7bb84 100644 --- a/VAR.ExpressionEvaluator/Tokenizer.cs +++ b/VAR.ExpressionEvaluator/Tokenizer.cs @@ -101,6 +101,15 @@ namespace VAR.ExpressionEvaluator _currentToken = Token.ParenthesisEnd; return; + case '!': + NextChar(); + _currentToken = Token.Not; + if (_currentChar == '=') + { + NextChar(); + _currentToken = Token.NotEquals; + } + return; case '=': NextChar(); _currentToken = Token.Equals; diff --git a/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj b/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj index 08ac712..978cbbb 100644 --- a/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj +++ b/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj @@ -44,8 +44,14 @@ + + + + + +