diff --git a/VAR.ExpressionEvaluator.Tests/ParserTests.cs b/VAR.ExpressionEvaluator.Tests/ParserTests.cs index 19e70c5..7124d30 100644 --- a/VAR.ExpressionEvaluator.Tests/ParserTests.cs +++ b/VAR.ExpressionEvaluator.Tests/ParserTests.cs @@ -324,5 +324,48 @@ namespace VAR.ExpressionEvaluator.Tests #endregion Relations + #region BooleanOps + + [TestMethod()] + public void BooleanOps_And() + { + Assert.AreEqual(false, Parser.EvaluateString("false and false")); + Assert.AreEqual(false, Parser.EvaluateString("false and true")); + Assert.AreEqual(false, Parser.EvaluateString("true and false")); + Assert.AreEqual(true, Parser.EvaluateString("true and true")); + + Assert.AreEqual(false, Parser.EvaluateString("false && false")); + Assert.AreEqual(false, Parser.EvaluateString("false && true")); + Assert.AreEqual(false, Parser.EvaluateString("true && false")); + Assert.AreEqual(true, Parser.EvaluateString("true && true")); + } + + [TestMethod()] + public void BooleanOps_Or() + { + Assert.AreEqual(false, Parser.EvaluateString("false or false")); + Assert.AreEqual(true, Parser.EvaluateString("false or true")); + Assert.AreEqual(true, Parser.EvaluateString("true or false")); + Assert.AreEqual(true, Parser.EvaluateString("true or true")); + + Assert.AreEqual(false, Parser.EvaluateString("false || false")); + Assert.AreEqual(true, Parser.EvaluateString("false || true")); + Assert.AreEqual(true, Parser.EvaluateString("true || false")); + Assert.AreEqual(true, Parser.EvaluateString("true || true")); + } + + [TestMethod()] + public void BooleanOps_Not() + { + Assert.AreEqual(true, Parser.EvaluateString("!false")); + Assert.AreEqual(false, Parser.EvaluateString("!true")); + + Assert.AreEqual(true, Parser.EvaluateString("not false")); + Assert.AreEqual(false, Parser.EvaluateString("not true")); + } + + + #endregion BooleanOps + } } \ No newline at end of file diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanAndNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanAndNode.cs new file mode 100644 index 0000000..dca5046 --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanAndNode.cs @@ -0,0 +1,17 @@ +namespace VAR.ExpressionEvaluator +{ + public class ExpressionBooleanAndNode : ExpressionBinaryNode + { + public ExpressionBooleanAndNode(IExpressionNode leftNode, IExpressionNode rightNode) : + base(leftNode, rightNode, BooleanAndOp) + { + } + + private static object BooleanAndOp(object leftValue, object rightValue) + { + bool bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue); + bool brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue); + return bLeftValue && brightValue; + } + } +} diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNode.cs index eea9494..cc4efae 100644 --- a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNode.cs +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNode.cs @@ -13,5 +13,30 @@ { return _value; } + + public static bool ConvertToBoolean(object value) + { + if (value is bool) + { + return (bool)value; + } + if (value is decimal) + { + return (decimal)value == 0; + } + if (value is string) + { + string str = (string)value; + if (string.IsNullOrEmpty(str) || str == "0" || str.ToLower() == "false") + { + return false; + } + else + { + return true; + } + } + return false; + } } } diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNotNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNotNode.cs new file mode 100644 index 0000000..ebb06cb --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanNotNode.cs @@ -0,0 +1,16 @@ +namespace VAR.ExpressionEvaluator +{ + public class ExpressionBooleanNotNode : ExpressionUnaryNode + { + public ExpressionBooleanNotNode(IExpressionNode node) : + base(node, BooleanNotOp) + { + } + + private static object BooleanNotOp(object value) + { + value = ExpressionBooleanNode.ConvertToBoolean(value); + return !(bool)value; + } + } +} diff --git a/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanOrNode.cs b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanOrNode.cs new file mode 100644 index 0000000..d0127db --- /dev/null +++ b/VAR.ExpressionEvaluator/ExpressionNodes/ExpressionBooleanOrNode.cs @@ -0,0 +1,17 @@ +namespace VAR.ExpressionEvaluator +{ + public class ExpressionBooleanOrNode : ExpressionBinaryNode + { + public ExpressionBooleanOrNode(IExpressionNode leftNode, IExpressionNode rightNode) : + base(leftNode, rightNode, BooleanOrOp) + { + } + + private static object BooleanOrOp(object leftValue, object rightValue) + { + bool bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue); + bool brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue); + return bLeftValue || brightValue; + } + } +} diff --git a/VAR.ExpressionEvaluator/Parser.cs b/VAR.ExpressionEvaluator/Parser.cs index 9a489b3..fa03e96 100644 --- a/VAR.ExpressionEvaluator/Parser.cs +++ b/VAR.ExpressionEvaluator/Parser.cs @@ -15,7 +15,7 @@ namespace VAR.ExpressionEvaluator public IExpressionNode ParseExpression() { - var expr = ParseRelations(); + var expr = ParseBooleanOp(); if (_tokenizer.Token != Token.EOF) { @@ -25,6 +25,36 @@ namespace VAR.ExpressionEvaluator return expr; } + private IExpressionNode ParseBooleanOp() + { + if (_tokenizer.Token == Token.Not) + { + _tokenizer.NextToken(); + var node = ParseBooleanOp(); + return new ExpressionBooleanNotNode(node); + } + IExpressionNode leftNode = ParseRelations(); + while (true) + { + if (_tokenizer.Token == Token.And) + { + _tokenizer.NextToken(); + IExpressionNode rightNode = ParseRelations(); + leftNode = new ExpressionBooleanAndNode(leftNode, rightNode); + } + if (_tokenizer.Token == Token.Or) + { + _tokenizer.NextToken(); + IExpressionNode rightNode = ParseRelations(); + leftNode = new ExpressionBooleanOrNode(leftNode, rightNode); + } + else + { + return leftNode; + } + } + } + private IExpressionNode ParseRelations() { IExpressionNode leftNode = ParsePlusAndMinus(); @@ -99,20 +129,20 @@ namespace VAR.ExpressionEvaluator private IExpressionNode ParseMultiplyDivision() { - IExpressionNode lhs = ParseUnary(); + IExpressionNode lhs = ParseNumericSign(); while (true) { if (_tokenizer.Token == Token.Multiply) { _tokenizer.NextToken(); - IExpressionNode rhs = ParseUnary(); + IExpressionNode rhs = ParseNumericSign(); lhs = new ExpressionMultiplyNode(lhs, rhs); } else if (_tokenizer.Token == Token.Division) { _tokenizer.NextToken(); - IExpressionNode rhs = ParseUnary(); + IExpressionNode rhs = ParseNumericSign(); lhs = new ExpressionDivisionNode(lhs, rhs); } else @@ -122,17 +152,17 @@ namespace VAR.ExpressionEvaluator } } - private IExpressionNode ParseUnary() + private IExpressionNode ParseNumericSign() { if (_tokenizer.Token == Token.Plus) { _tokenizer.NextToken(); - return ParseUnary(); + return ParseNumericSign(); } if (_tokenizer.Token == Token.Minus) { _tokenizer.NextToken(); - var node = ParseUnary(); + var node = ParseNumericSign(); return new ExpressionNumberNegateNode(node); } return ParseTerminus(); @@ -203,7 +233,7 @@ namespace VAR.ExpressionEvaluator if (_tokenizer.Token == Token.ParenthesisStart) { _tokenizer.NextToken(); - IExpressionNode node = ParseRelations(); + IExpressionNode node = ParseBooleanOp(); if (_tokenizer.Token != Token.ParenthesisEnd) { throw new Exception("Missing close parenthesis"); diff --git a/VAR.ExpressionEvaluator/Token.cs b/VAR.ExpressionEvaluator/Token.cs index 3d39201..816dc68 100644 --- a/VAR.ExpressionEvaluator/Token.cs +++ b/VAR.ExpressionEvaluator/Token.cs @@ -17,6 +17,8 @@ GreaterOrEqualThan, LessThan, LessOrEqualThan, + And, + Or, Comma, Identifier, String, diff --git a/VAR.ExpressionEvaluator/Tokenizer.cs b/VAR.ExpressionEvaluator/Tokenizer.cs index ac7bb84..c0512fd 100644 --- a/VAR.ExpressionEvaluator/Tokenizer.cs +++ b/VAR.ExpressionEvaluator/Tokenizer.cs @@ -110,6 +110,7 @@ namespace VAR.ExpressionEvaluator _currentToken = Token.NotEquals; } return; + case '=': NextChar(); _currentToken = Token.Equals; @@ -124,6 +125,7 @@ namespace VAR.ExpressionEvaluator } return; + case '>': NextChar(); _currentToken = Token.GreaterThan; @@ -144,6 +146,24 @@ namespace VAR.ExpressionEvaluator } return; + case '&': + NextChar(); + _currentToken = Token.And; + while (_currentChar == '&') + { + NextChar(); + } + return; + + case '|': + NextChar(); + _currentToken = Token.Or; + while (_currentChar == '|') + { + NextChar(); + } + return; + case ',': NextChar(); _currentToken = Token.Comma; @@ -161,7 +181,23 @@ namespace VAR.ExpressionEvaluator if (_currentChar == '\0') { break; } } _text = sb.ToString(); - _currentToken = Token.Identifier; + string textLowercase = _text.ToLower(); + if (textLowercase == "and") + { + _currentToken = Token.And; + } + else if (textLowercase == "or") + { + _currentToken = Token.Or; + } + else if (textLowercase == "not") + { + _currentToken = Token.Not; + } + else + { + _currentToken = Token.Identifier; + } return; } diff --git a/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj b/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj index 978cbbb..e8e2533 100644 --- a/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj +++ b/VAR.ExpressionEvaluator/VAR.ExpressionEvaluator.csproj @@ -42,7 +42,10 @@ + + +