Parser: Boolean operations

This commit is contained in:
2019-12-02 03:02:53 +01:00
parent 4f3e5a325e
commit 703639aa07
9 changed files with 198 additions and 9 deletions

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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");

View File

@@ -17,6 +17,8 @@
GreaterOrEqualThan,
LessThan,
LessOrEqualThan,
And,
Or,
Comma,
Identifier,
String,

View File

@@ -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();
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;
}

View File

@@ -42,7 +42,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="EvaluationContext.cs" />
<Compile Include="ExpressionNodes\ExpressionBooleanAndNode.cs" />
<Compile Include="ExpressionNodes\ExpressionBooleanNode.cs" />
<Compile Include="ExpressionNodes\ExpressionBooleanNotNode.cs" />
<Compile Include="ExpressionNodes\ExpressionBooleanOrNode.cs" />
<Compile Include="ExpressionNodes\ExpressionDivisionNode.cs" />
<Compile Include="ExpressionNodes\ExpressionEqualsNode.cs" />
<Compile Include="ExpressionNodes\ExpressionFunctionNode.cs" />