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 #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; 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() public IExpressionNode ParseExpression()
{ {
var expr = ParseRelations(); var expr = ParseBooleanOp();
if (_tokenizer.Token != Token.EOF) if (_tokenizer.Token != Token.EOF)
{ {
@@ -25,6 +25,36 @@ namespace VAR.ExpressionEvaluator
return expr; 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() private IExpressionNode ParseRelations()
{ {
IExpressionNode leftNode = ParsePlusAndMinus(); IExpressionNode leftNode = ParsePlusAndMinus();
@@ -99,20 +129,20 @@ namespace VAR.ExpressionEvaluator
private IExpressionNode ParseMultiplyDivision() private IExpressionNode ParseMultiplyDivision()
{ {
IExpressionNode lhs = ParseUnary(); IExpressionNode lhs = ParseNumericSign();
while (true) while (true)
{ {
if (_tokenizer.Token == Token.Multiply) if (_tokenizer.Token == Token.Multiply)
{ {
_tokenizer.NextToken(); _tokenizer.NextToken();
IExpressionNode rhs = ParseUnary(); IExpressionNode rhs = ParseNumericSign();
lhs = new ExpressionMultiplyNode(lhs, rhs); lhs = new ExpressionMultiplyNode(lhs, rhs);
} }
else if (_tokenizer.Token == Token.Division) else if (_tokenizer.Token == Token.Division)
{ {
_tokenizer.NextToken(); _tokenizer.NextToken();
IExpressionNode rhs = ParseUnary(); IExpressionNode rhs = ParseNumericSign();
lhs = new ExpressionDivisionNode(lhs, rhs); lhs = new ExpressionDivisionNode(lhs, rhs);
} }
else else
@@ -122,17 +152,17 @@ namespace VAR.ExpressionEvaluator
} }
} }
private IExpressionNode ParseUnary() private IExpressionNode ParseNumericSign()
{ {
if (_tokenizer.Token == Token.Plus) if (_tokenizer.Token == Token.Plus)
{ {
_tokenizer.NextToken(); _tokenizer.NextToken();
return ParseUnary(); return ParseNumericSign();
} }
if (_tokenizer.Token == Token.Minus) if (_tokenizer.Token == Token.Minus)
{ {
_tokenizer.NextToken(); _tokenizer.NextToken();
var node = ParseUnary(); var node = ParseNumericSign();
return new ExpressionNumberNegateNode(node); return new ExpressionNumberNegateNode(node);
} }
return ParseTerminus(); return ParseTerminus();
@@ -203,7 +233,7 @@ namespace VAR.ExpressionEvaluator
if (_tokenizer.Token == Token.ParenthesisStart) if (_tokenizer.Token == Token.ParenthesisStart)
{ {
_tokenizer.NextToken(); _tokenizer.NextToken();
IExpressionNode node = ParseRelations(); IExpressionNode node = ParseBooleanOp();
if (_tokenizer.Token != Token.ParenthesisEnd) if (_tokenizer.Token != Token.ParenthesisEnd)
{ {
throw new Exception("Missing close parenthesis"); throw new Exception("Missing close parenthesis");

View File

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

View File

@@ -110,6 +110,7 @@ namespace VAR.ExpressionEvaluator
_currentToken = Token.NotEquals; _currentToken = Token.NotEquals;
} }
return; return;
case '=': case '=':
NextChar(); NextChar();
_currentToken = Token.Equals; _currentToken = Token.Equals;
@@ -124,6 +125,7 @@ namespace VAR.ExpressionEvaluator
} }
return; return;
case '>': case '>':
NextChar(); NextChar();
_currentToken = Token.GreaterThan; _currentToken = Token.GreaterThan;
@@ -144,6 +146,24 @@ namespace VAR.ExpressionEvaluator
} }
return; return;
case '&':
NextChar();
_currentToken = Token.And;
while (_currentChar == '&')
{
NextChar();
}
return;
case '|':
NextChar();
_currentToken = Token.Or;
while (_currentChar == '|')
{
NextChar();
}
return;
case ',': case ',':
NextChar(); NextChar();
_currentToken = Token.Comma; _currentToken = Token.Comma;
@@ -161,7 +181,23 @@ namespace VAR.ExpressionEvaluator
if (_currentChar == '\0') { break; } if (_currentChar == '\0') { break; }
} }
_text = sb.ToString(); _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; _currentToken = Token.Identifier;
}
return; return;
} }

View File

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