4 Commits
0.1.1 ... 0_2_0

Author SHA1 Message Date
4bcfe85de1 0.2.0 2019-12-02 16:03:39 +01:00
c9479cf079 Allow null values on nodes. 2019-12-02 15:59:37 +01:00
213bb424f7 Tokenizer: Identifiers inside '[' and ']' 2019-12-02 15:51:43 +01:00
2dc0ed7ba3 Parser: Fix recursion bug. 2019-12-02 12:05:56 +01:00
27 changed files with 223 additions and 30 deletions

8
SharedAssemblyInfo.cs Normal file
View File

@@ -0,0 +1,8 @@
using System.Reflection;
[assembly: AssemblyCompany("VAR")]
[assembly: AssemblyCopyright("Copyright © VAR 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("0.2.0.0")]
[assembly: AssemblyFileVersion("0.2.0.0")]

View File

@@ -1,6 +1,6 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace VAR.ExpressionEvaluator.Tests
{
@@ -199,6 +199,29 @@ namespace VAR.ExpressionEvaluator.Tests
Assert.AreEqual(10m, result);
}
[TestMethod()]
public void Functions__NestedTest()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("linea1", 1);
evaluationContex.SetVariable("linea2", 1);
evaluationContex.SetVariable("linea4", 4);
evaluationContex.SetFunction("iif", (parameters) =>
{
if (((bool)parameters[0]) == true)
{
return parameters[1];
}
else
{
return parameters[2];
}
});
string expression = "iif(linea1>linea2,iif(linea1>linea4, linea1, iif(linea4>linea2,linea4,linea2)),iif(linea2>linea4,linea2,linea4))";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.AreEqual(4m, result);
}
#endregion Functions
#region Strings
@@ -378,5 +401,45 @@ namespace VAR.ExpressionEvaluator.Tests
#endregion BooleanOps
#region Null value
[TestMethod()]
public void NullValue_NullPlusAnything_EqualsNull()
{
Assert.AreEqual(null, Parser.EvaluateString("null + 1"));
Assert.AreEqual(null, Parser.EvaluateString("null + 100"));
Assert.AreEqual(null, Parser.EvaluateString("1 + null"));
Assert.AreEqual(null, Parser.EvaluateString("100 + null"));
Assert.AreEqual(null, Parser.EvaluateString("null + null"));
}
[TestMethod()]
public void NullValue_NullMinusAnything_EqualsNull()
{
Assert.AreEqual(null, Parser.EvaluateString("null - 1"));
Assert.AreEqual(null, Parser.EvaluateString("null - 100"));
Assert.AreEqual(null, Parser.EvaluateString("1 - null"));
Assert.AreEqual(null, Parser.EvaluateString("100 - null"));
Assert.AreEqual(null, Parser.EvaluateString("null - null"));
}
[TestMethod()]
public void NullValue_NullByAnything_EqualsNull()
{
Assert.AreEqual(null, Parser.EvaluateString("null * 1"));
Assert.AreEqual(null, Parser.EvaluateString("null * 100"));
Assert.AreEqual(null, Parser.EvaluateString("1 * null"));
Assert.AreEqual(null, Parser.EvaluateString("100 * null"));
Assert.AreEqual(null, Parser.EvaluateString("null * null"));
Assert.AreEqual(null, Parser.EvaluateString("null / 1"));
Assert.AreEqual(null, Parser.EvaluateString("null / 100"));
Assert.AreEqual(null, Parser.EvaluateString("1 / null"));
Assert.AreEqual(null, Parser.EvaluateString("100 / null"));
Assert.AreEqual(null, Parser.EvaluateString("null / null"));
}
#endregion
}
}

View File

@@ -4,12 +4,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VAR.ExpressionEvaluator.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("VAR")]
[assembly: AssemblyProduct("VAR.ExpressionEvaluator.Tests")]
[assembly: AssemblyCopyright("Copyright © VAR 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("3195f0e0-9489-49ce-8fc2-627a73f80cc7")]
[assembly: AssemblyVersion("0.1.1.0")]
[assembly: AssemblyFileVersion("0.1.1.0")]

View File

@@ -1,5 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace VAR.ExpressionEvaluator.Tests
{
@@ -92,7 +92,7 @@ namespace VAR.ExpressionEvaluator.Tests
[TestMethod()]
public void Tokenizer__Identifiers()
{
var testString = "null true false _$variable1 $variable2";
var testString = "null true false _$variable1 $variable2 [;{}#]";
var t = new Tokenizer(new StringReader(testString));
// "null"
@@ -120,6 +120,11 @@ namespace VAR.ExpressionEvaluator.Tests
Assert.AreEqual(t.Text, "$variable2");
t.NextToken();
// ";{}#"
Assert.AreEqual(t.Token, Token.Identifier);
Assert.AreEqual(t.Text, ";{}#");
t.NextToken();
Assert.AreEqual(t.Token, Token.EOF);
}

View File

@@ -51,6 +51,9 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="..\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="ExpressionBinaryNodeTests.cs" />
<Compile Include="ExpressionNumberNodeTests.cs" />
<Compile Include="ExpressionStringNodeTests.cs" />

View File

@@ -39,6 +39,11 @@ namespace VAR.ExpressionEvaluator
public void SetVariable(string name, object value)
{
if (value is DBNull)
{
value = null;
}
if (value is DateTime)
{
value = ((DateTime)value).ToString("s");
@@ -60,7 +65,7 @@ namespace VAR.ExpressionEvaluator
{
if (_variables.ContainsKey(name) == false)
{
return null;
throw new Exception(string.Format("Variable {0} not found", name));
}
return _variables[name];
}

View File

@@ -9,9 +9,10 @@
private static object BooleanAndOp(object leftValue, object rightValue)
{
bool bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue);
bool brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue);
return bLeftValue && brightValue;
bool? bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue);
bool? brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue);
if (bLeftValue == null || bLeftValue == null) { return null; }
return bLeftValue.Value && brightValue.Value;
}
}
}

View File

@@ -14,8 +14,13 @@
return _value;
}
public static bool ConvertToBoolean(object value)
public static bool? ConvertToBoolean(object value)
{
if (value == null)
{
return null;
}
if (value is bool)
{
return (bool)value;

View File

@@ -10,6 +10,7 @@
private static object BooleanNotOp(object value)
{
value = ExpressionBooleanNode.ConvertToBoolean(value);
if (value == null) { return null; }
return !(bool)value;
}
}

View File

@@ -9,9 +9,10 @@
private static object BooleanOrOp(object leftValue, object rightValue)
{
bool bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue);
bool brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue);
return bLeftValue || brightValue;
bool? bLeftValue = ExpressionBooleanNode.ConvertToBoolean(leftValue);
bool? brightValue = ExpressionBooleanNode.ConvertToBoolean(rightValue);
if (bLeftValue == null || bLeftValue == null) { return null; }
return bLeftValue.Value || brightValue.Value;
}
}
}

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object DivisionOp(object leftValue, object rightValue)
{
if (leftValue == null || rightValue == null)
{
return null;
}
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)

View File

@@ -11,6 +11,15 @@ namespace VAR.ExpressionEvaluator
private static object EqualsOp(object leftValue, object rightValue)
{
if (leftValue == null && rightValue == null)
{
return true;
}
if (leftValue == null || rightValue == null)
{
return false;
}
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) == 0;

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object GreaterOrEqualThanOp(object leftValue, object rightValue)
{
if (leftValue == null || rightValue == null)
{
return false;
}
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) >= 0;

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object GreaterThanOp(object leftValue, object rightValue)
{
if (leftValue == null || rightValue == null)
{
return false;
}
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) > 0;

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object LessOrEqualThanOp(object leftValue, object rightValue)
{
if (leftValue == null || rightValue == null)
{
return false;
}
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) <= 0;

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object LessThanOp(object leftValue, object rightValue)
{
if (leftValue == null || rightValue == null)
{
return false;
}
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) < 0;

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object MinusOp(object leftValue, object rightValue)
{
if (leftValue == null || rightValue == null)
{
return null;
}
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object MultiplyOp(object leftValue, object rightValue)
{
if (leftValue == null || rightValue == null)
{
return null;
}
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)

View File

@@ -11,6 +11,15 @@ namespace VAR.ExpressionEvaluator
private static object NotEqualsOp(object leftValue, object rightValue)
{
if (leftValue == null && rightValue == null)
{
return false;
}
if (leftValue == null || rightValue == null)
{
return true;
}
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) != 0;

View File

@@ -0,0 +1,10 @@
namespace VAR.ExpressionEvaluator
{
public class ExpressionNullNode : IExpressionNode
{
public object Eval(IEvaluationContext evaluationContext)
{
return null;
}
}
}

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object NumberNegateOp(object value)
{
if (value == null)
{
return null;
}
if (value is string)
{
if (decimal.TryParse((string)value, out decimal dec) == false)

View File

@@ -11,6 +11,11 @@ namespace VAR.ExpressionEvaluator
private static object PlusOp(object leftValue, object rightValue)
{
if (leftValue == null || rightValue == null)
{
return null;
}
if (leftValue is string || rightValue is string)
{
return string.Concat(Convert.ToString(leftValue), Convert.ToString(rightValue));

View File

@@ -14,10 +14,6 @@ namespace VAR.ExpressionEvaluator
public object Eval(IEvaluationContext evaluationContext)
{
object value = evaluationContext.GetVariable(_name);
if (value == null)
{
throw new Exception(string.Format("Variable {0} not found", _name));
}
return value;
}
}

View File

@@ -197,6 +197,11 @@ namespace VAR.ExpressionEvaluator
_tokenizer.NextToken();
return new ExpressionBooleanNode(false);
}
if (identifierToLower == "null")
{
_tokenizer.NextToken();
return new ExpressionNullNode();
}
string identifier = _tokenizer.Text;
_tokenizer.NextToken();
@@ -211,7 +216,7 @@ namespace VAR.ExpressionEvaluator
var parameters = new List<IExpressionNode>();
while (true)
{
parameters.Add(ParsePlusAndMinus());
parameters.Add(ParseBooleanOp());
if (_tokenizer.Token == Token.Comma)
{
_tokenizer.NextToken();

View File

@@ -4,12 +4,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VAR.ExpressionEvaluator")]
[assembly: AssemblyDescription("Expression Evaluation Library")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("VAR")]
[assembly: AssemblyProduct("VAR.ExpressionEvaluator")]
[assembly: AssemblyCopyright("Copyright © VAR 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("74635f68-55b1-4819-84a3-9ea818d396d9")]
[assembly: AssemblyVersion("0.1.1.0")]
[assembly: AssemblyFileVersion("0.1.1.0")]

View File

@@ -181,6 +181,41 @@ namespace VAR.ExpressionEvaluator
if (_currentChar == '\0') { break; }
}
_text = sb.ToString();
_currentToken = Token.Identifier;
}
else if (_currentChar == '[')
{
char stringEndsWith = ']';
NextChar();
StringBuilder sbString = new StringBuilder();
while (_currentChar != stringEndsWith && _currentChar != '\0')
{
if (_currentChar != '\\')
{
sbString.Append(_currentChar);
}
else
{
NextChar();
if (_currentChar == stringEndsWith)
{
sbString.Append(stringEndsWith);
}
else
{
// FIXME: Other escaped characters
sbString.Append(_currentChar);
}
}
NextChar();
}
NextChar();
_text = sbString.ToString();
_currentToken = Token.Identifier;
}
if (_currentToken == Token.Identifier)
{
string textLowercase = _text.ToLower();
if (textLowercase == "and")
{

View File

@@ -41,6 +41,9 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="EvaluationContext.cs" />
<Compile Include="ExpressionNodes\ExpressionBooleanAndNode.cs" />
<Compile Include="ExpressionNodes\ExpressionBooleanNode.cs" />
@@ -55,6 +58,7 @@
<Compile Include="ExpressionNodes\ExpressionLessThanNode.cs" />
<Compile Include="ExpressionNodes\ExpressionMultiplyNode.cs" />
<Compile Include="ExpressionNodes\ExpressionNotEqualsNode.cs" />
<Compile Include="ExpressionNodes\ExpressionNullNode.cs" />
<Compile Include="ExpressionNodes\ExpressionUnaryNode.cs" />
<Compile Include="ExpressionNodes\ExpressionBinaryNode.cs" />
<Compile Include="ExpressionNodes\ExpressionMinusNode.cs" />