25 Commits
0.1.1 ... Main

Author SHA1 Message Date
63bc30aa7f Sync 2025-08-01 01:16:35 +02:00
44f3d58db4 0.2.8 2022-03-01 19:14:46 +01:00
551da30a7e Fix functions without parameters. 2022-03-01 19:13:57 +01:00
e7736f26b7 0.2.7 2021-11-18 11:32:56 +01:00
1a878fe4e7 Fix usages of decimal.TryParse 2021-11-18 11:32:07 +01:00
75f5c948ed 0.2.6 2021-10-14 20:11:10 +02:00
ad9e3e6bad Parser.ParseBooleanOp: Parse failure with multiple "And" "Or" chained 2021-10-14 20:07:36 +02:00
953a9340cc 0.2.5 2021-09-28 20:25:52 +02:00
0440b418cb ExpressionEqualsNode: Rewrite to be less strict 2021-09-28 20:24:14 +02:00
57c5f677f5 Update copyright on LICENSE.txt 2021-09-28 20:04:10 +02:00
93750dad17 0.2.4 2021-09-22 03:37:28 +02:00
c0c0ae8017 Fix ExpressionEqualsNode not comparing null and string 2021-09-22 03:34:39 +02:00
fa13af7f82 Rearrangements
Migrate to Sdk projects.
Migrate tests to xUnit.
Bump version 0.2.3.
Migrate to NetStandar2.0.
2021-06-13 20:16:59 +02:00
196131a66b Custom Exceptions + Tests. 2020-11-25 21:58:28 +01:00
b31d2ba2f3 Some tests for decimal numbers. 2020-11-25 21:32:29 +01:00
67b94392e0 Fix bug tokenizing an isolated "." 2020-11-25 20:35:32 +01:00
d6b5408f8a 0.2.2 2020-04-03 13:52:08 +02:00
f0984520f4 Handle strings on boolean operators. 2020-04-03 13:51:06 +02:00
101ec64e95 0.2.1 2019-12-03 10:02:55 +01:00
4eca26daca Tokenizer: Accept "<>" as NotEqual token. 2019-12-03 10:02:20 +01:00
6e9b8c9826 Fix Build.Nuget.cmd for Visual Studio 2017 Enterprise. 2019-12-03 09:59:36 +01:00
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
39 changed files with 1210 additions and 543 deletions

13
.idea/.idea.VAR.ExpressionEvaluator/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/.idea.VAR.ExpressionEvaluator.iml
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

21
LICENSE.txt Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2019-2021 Valeriano Alfonso Rodriguez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

26
README.md Normal file
View File

@@ -0,0 +1,26 @@
# Expression Evaluation Library
## Usage
### VAR.ExpressionEvaluator
Add the resulting assembly as reference in your projects, and this line on code:
```csharp
using VAR.ExpressionEvaluator;
```
## Building
A Visual Studio solution is provided. Simply, click build on the IDE.
The build generates a DLL and a Nuget package.
## Contributing
1. Fork it!
2. Create your feature branch: `git checkout -b my-new-feature`
3. Commit your changes: `git commit -am 'Add some feature'`
4. Push to the branch: `git push origin my-new-feature`
5. Submit a pull request :D
## Credits
* Valeriano Alfonso Rodriguez.

View File

@@ -1,11 +1,10 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Xunit;
namespace VAR.ExpressionEvaluator.Tests
{
[TestClass()]
public class ExpressionBinaryNodeTests
{
[TestMethod()]
[Fact]
public void ExpressionBinaryNode__Plus()
{
var expr = new ExpressionBinaryNode(
@@ -16,10 +15,10 @@ namespace VAR.ExpressionEvaluator.Tests
var result = expr.Eval(null);
Assert.AreEqual(30m, result);
Assert.Equal(30m, result);
}
[TestMethod()]
[Fact]
public void ExpressionBinaryNode__Minus()
{
var expr = new ExpressionBinaryNode(
@@ -30,10 +29,10 @@ namespace VAR.ExpressionEvaluator.Tests
var result = expr.Eval(null);
Assert.AreEqual(-10m, result);
Assert.Equal(-10m, result);
}
[TestMethod()]
[Fact]
public void ExpressionBinaryNode__Multiply()
{
var expr = new ExpressionBinaryNode(
@@ -44,10 +43,10 @@ namespace VAR.ExpressionEvaluator.Tests
var result = expr.Eval(null);
Assert.AreEqual(200m, result);
Assert.Equal(200m, result);
}
[TestMethod()]
[Fact]
public void ExpressionBinaryNode__Division()
{
var expr = new ExpressionBinaryNode(
@@ -58,7 +57,7 @@ namespace VAR.ExpressionEvaluator.Tests
var result = expr.Eval(null);
Assert.AreEqual(2m, result);
Assert.Equal(2m, result);
}
}
}

View File

@@ -1,29 +1,28 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Xunit;
namespace VAR.ExpressionEvaluator.Tests
{
[TestClass()]
public class ExpressionNumberNodeTests
{
[TestMethod()]
[Fact]
public void ExpressionNumberNode__One()
{
IExpressionNode node = new ExpressionNumberNode(1);
Assert.AreEqual(1m, node.Eval(null));
Assert.Equal(1m, node.Eval(null));
}
[TestMethod()]
[Fact]
public void ExpressionNumberNode__Two()
{
IExpressionNode node = new ExpressionNumberNode(2);
Assert.AreEqual(2m, node.Eval(null));
Assert.Equal(2m, node.Eval(null));
}
[TestMethod()]
[Fact]
public void ExpressionNumberNode__OneHundredDotFortyFive()
{
IExpressionNode node = new ExpressionNumberNode(100.45m);
Assert.AreEqual(100.45m, node.Eval(null));
Assert.Equal(100.45m, node.Eval(null));
}
}
}

View File

@@ -1,29 +1,28 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Xunit;
namespace VAR.ExpressionEvaluator.Tests
{
[TestClass()]
public class ExpressionStringNodeTests
{
[TestMethod()]
[Fact]
public void ExpressionNumberNode__Hello()
{
IExpressionNode node = new ExpressionStringNode("Hello");
Assert.AreEqual("Hello", node.Eval(null));
Assert.Equal("Hello", node.Eval(null));
}
[TestMethod()]
[Fact]
public void ExpressionNumberNode__World()
{
IExpressionNode node = new ExpressionStringNode("World");
Assert.AreEqual("World", node.Eval(null));
Assert.Equal("World", node.Eval(null));
}
[TestMethod()]
[Fact]
public void ExpressionNumberNode__Hello_World()
{
IExpressionNode node = new ExpressionStringNode("Hello World");
Assert.AreEqual("Hello World", node.Eval(null));
Assert.Equal("Hello World", node.Eval(null));
}
}
}

View File

@@ -1,155 +1,174 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System;
using System.Linq;
using Xunit;
namespace VAR.ExpressionEvaluator.Tests
{
[TestClass()]
public class ParserTests
{
#region Plus and Minus
[TestMethod()]
[Fact]
public void PlusAndMinus__Ten_EqualsTen()
{
string expression = "10";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(10m, result);
Assert.Equal(10m, result);
}
[TestMethod()]
[Fact]
public void PlusAndMinus__OnePlusTwo_EqualsThree()
{
string expression = "1 + 2";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(3m, result);
Assert.Equal(3m, result);
}
[TestMethod()]
[Fact]
public void PlusAndMinus__OneMinusTwo_EqualsMinusOne()
{
string expression = "1 - 2";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(-1m, result);
Assert.Equal(-1m, result);
}
[TestMethod()]
[Fact]
public void PlusAndMinus__OneMillionMinusHundredThousands_EqualsNineHundredThousands()
{
string expression = "1000000 - 100000";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(900000m, result);
Assert.Equal(900000m, result);
}
#endregion Plus and minus
#region Number signs
[TestMethod()]
[Fact]
public void Signs__MinusTen()
{
string expression = "-10";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(-10m, result);
Assert.Equal(-10m, result);
}
[TestMethod()]
[Fact]
public void Signs__PlusTen()
{
string expression = "+10";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(10m, result);
Assert.Equal(10m, result);
}
[TestMethod()]
[Fact]
public void Signs__MinusMinusTen()
{
string expression = "--10";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(10m, result);
Assert.Equal(10m, result);
}
[TestMethod()]
[Fact]
public void Signs__MinusPlusChainTen()
{
string expression = "--++-+-10";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(10m, result);
Assert.Equal(10m, result);
}
[TestMethod()]
[Fact]
public void Signs__10Minus20Minus30()
{
string expression = "10 + -20 - +30";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(-40m, result);
Assert.Equal(-40m, result);
}
#endregion Number signs
#region Decimal numbers
[Fact]
public void Decimals__OnePointZero()
{
string expression = "1.0";
object result = Parser.EvaluateString(expression);
Assert.Equal(1.0m, result);
}
[Fact]
public void Decimals__OnePointOne()
{
string expression = "1.1";
object result = Parser.EvaluateString(expression);
Assert.Equal(1.1m, result);
}
#endregion Decimal numbers
#region Multiplication and division
[TestMethod()]
[Fact]
public void MultAndDiv__10MutiplyBy2()
{
string expression = "10 * 2";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(20m, result);
Assert.Equal(20m, result);
}
[TestMethod()]
[Fact]
public void MultAndDiv__10DividedBy2()
{
string expression = "10 / 2";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(5m, result);
Assert.Equal(5m, result);
}
[TestMethod()]
[Fact]
public void MultAndDiv__5DividedBy2()
{
string expression = "5 / 2";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(2.5m, result);
Assert.Equal(2.5m, result);
}
[TestMethod()]
[Fact]
public void MultAndDiv__5DividedBy2Plus1()
{
string expression = "5 / 2 + 1";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(3.5m, result);
Assert.Equal(3.5m, result);
}
[TestMethod()]
[Fact]
public void MultAndDiv__1Plus5DividedBy2()
{
string expression = "1 + 5 / 2";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(3.5m, result);
Assert.Equal(3.5m, result);
}
[TestMethod()]
[Fact]
public void MultAndDiv__5DividedByParen1Plus1()
{
string expression = "5 / (1 + 1)";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(2.5m, result);
Assert.Equal(2.5m, result);
}
[TestMethod()]
[Fact]
public void MultAndDiv__Paren2Plus2DividedByParen1Plus1()
{
string expression = "(2 + 2) / (1 + 1)";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(2m, result);
Assert.Equal(2m, result);
}
#endregion Multiplication and division
#region Variables
[TestMethod()]
[Fact]
public void Variables__Var1PlusVar2()
{
EvaluationContext evaluationContex = new EvaluationContext();
@@ -157,10 +176,10 @@ namespace VAR.ExpressionEvaluator.Tests
evaluationContex.SetVariable("v2", 1);
string expression = "v1 + v2";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.AreEqual(2m, result);
Assert.Equal(2m, result);
}
[TestMethod()]
[Fact]
public void Variables__Var1MultiplyVar2()
{
EvaluationContext evaluationContex = new EvaluationContext();
@@ -168,10 +187,10 @@ namespace VAR.ExpressionEvaluator.Tests
evaluationContex.SetVariable("v2", 5);
string expression = "v1 * v2";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.AreEqual(50m, result);
Assert.Equal(50m, result);
}
[TestMethod()]
[Fact]
public void Variables__Var1DivideVar2()
{
EvaluationContext evaluationContex = new EvaluationContext();
@@ -179,14 +198,14 @@ namespace VAR.ExpressionEvaluator.Tests
evaluationContex.SetVariable("$v2", 20);
string expression = "_v1 / $v2";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.AreEqual(5m, result);
Assert.Equal(5m, result);
}
#endregion Variables
#region Funcitions
#region Functions
[TestMethod()]
[Fact]
public void Functions__MaxFunction()
{
EvaluationContext evaluationContex = new EvaluationContext();
@@ -196,22 +215,66 @@ namespace VAR.ExpressionEvaluator.Tests
});
string expression = "max(1,2,10,5)";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.AreEqual(10m, result);
Assert.Equal(10m, result);
}
[Fact]
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.Equal(4m, result);
}
[Fact]
public void Functions__ZeroParameters()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetFunction("constant", (parameters) =>
{
return 9m;
});
string expression = "constant()";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.Equal(9m, result);
}
#endregion Functions
#region Strings
[TestMethod()]
[Fact]
public void Strings__Contatenate_Hello_World()
{
string expression = "\"Hello\" + ' ' +\"World\"";
object result = Parser.EvaluateString(expression);
Assert.AreEqual("Hello World", result);
Assert.Equal("Hello World", result);
}
[TestMethod()]
[Fact]
public void Strings__Contatenate_Hello_World_SimpleQuotes()
{
string expression = "'Hello' + ' ' +'World'";
object result = Parser.EvaluateString(expression);
Assert.Equal("Hello World", result);
}
[Fact]
public void Strings__Contatenate_Hello_World_WithVariables()
{
EvaluationContext evaluationContex = new EvaluationContext();
@@ -220,45 +283,45 @@ namespace VAR.ExpressionEvaluator.Tests
evaluationContex.SetVariable("v3", "World");
string expression = "v1 + v2 + v3";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.AreEqual("Hello World", result);
Assert.Equal("Hello World", result);
}
[TestMethod()]
[Fact]
public void Strings__Fail_Minus()
{
string expression = "'Hello' - 'World'";
try
{
object result = Parser.EvaluateString(expression);
Assert.Fail();
Assert.True(false);
}
catch (Exception)
{
}
}
[TestMethod()]
[Fact]
public void Strings__Fail_Multiply()
{
string expression = "'Hello' * 'World'";
try
{
object result = Parser.EvaluateString(expression);
Assert.Fail();
Assert.True(false);
}
catch (Exception)
{
}
}
[TestMethod()]
[Fact]
public void Strings__Fail_Division()
{
string expression = "'Hello' / 'World'";
try
{
object result = Parser.EvaluateString(expression);
Assert.Fail();
Assert.True(false);
}
catch (Exception)
{
@@ -269,114 +332,570 @@ namespace VAR.ExpressionEvaluator.Tests
#region Relations
[TestMethod()]
[Fact]
public void Relations_1GreatherThan2_EqualsFalse()
{
string expression = "1>2";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(false, result);
Assert.False((bool?)result);
}
[TestMethod()]
[Fact]
public void Relations_StringEmptyGreatherThan1_EqualsFalse()
{
string expression = "\"\">1";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_1GreatherThanStringEmpty_EqualsFalse()
{
string expression = "1>\"\"";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_1Equals1_EqualsTrue()
{
string expression = "1=1";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(true, result);
Assert.True((bool?)result);
}
[TestMethod()]
[Fact]
public void Relations_StringEmptyEquals1_EqualsFalse()
{
string expression = "\"\"=1";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_1EqualsStringEmpty_EqualsFalse()
{
string expression = "1=\"\"";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_10NotEquals1_EqualsTrue()
{
string expression = "10!=1";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(true, result);
Assert.True((bool?)result);
}
[TestMethod()]
[Fact]
public void Relations_StringEmptyNotEquals1_EqualsTrue()
{
string expression = "\"\"!=1";
object result = Parser.EvaluateString(expression);
Assert.True((bool?)result);
}
[Fact]
public void Relations_1NotEqualsStringEmpty_EqualsTrue()
{
string expression = "1!=\"\"";
object result = Parser.EvaluateString(expression);
Assert.True((bool?)result);
}
[Fact]
public void Relations_10Different1_EqualsTrue()
{
string expression = "10<>1";
object result = Parser.EvaluateString(expression);
Assert.True((bool?)result);
}
[Fact]
public void Relations_StringEmptyDifferent1_EqualsTrue()
{
string expression = "\"\"<>1";
object result = Parser.EvaluateString(expression);
Assert.True((bool?)result);
}
[Fact]
public void Relations_1DifferentStringEmpty_EqualsTrue()
{
string expression = "1<>\"\"";
object result = Parser.EvaluateString(expression);
Assert.True((bool?)result);
}
[Fact]
public void Relations_10LessThan1_EqualsFalse()
{
string expression = "10<1";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(false, result);
Assert.False((bool?)result);
}
[TestMethod()]
[Fact]
public void Relations_StringEmptyLessThan1_EqualsFalse()
{
string expression = "\"\"<1";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_1LessThanStringEmpty_EqualsFalse()
{
string expression = "1<\"\"";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_1GreaterOrEqualThan1_EqualsTrue()
{
string expression = "1>=1";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(true, result);
Assert.True((bool?)result);
}
[TestMethod()]
[Fact]
public void Relations_StringEmptyGreaterOrEqualThan1_EqualsFalse()
{
string expression = "\"\">=1";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_1GreaterOrEqualThanStringEmpty_EqualsFalse()
{
string expression = "1>=\"\"";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_1LessOrEqualThan1_EqualsTrue()
{
string expression = "1<=1";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(true, result);
Assert.True((bool?)result);
}
[TestMethod()]
[Fact]
public void Relations_1GreaterOrEqualThan10_EqualsFalse()
{
string expression = "1>=10";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(false, result);
Assert.False((bool?)result);
}
[TestMethod()]
[Fact]
public void Relations_10LessOrEqualThan1_EqualsFalse()
{
string expression = "10<=1";
object result = Parser.EvaluateString(expression);
Assert.AreEqual(false, result);
Assert.False((bool?)result);
}
[Fact]
public void Relations_StringEmptyLessOrEqualThan1_EqualsFalse()
{
string expression = "\"\"<=1";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact]
public void Relations_1LessOrEqualThanStringEmpty_EqualsFalse()
{
string expression = "1<=\"\"";
object result = Parser.EvaluateString(expression);
Assert.False((bool?)result);
}
[Fact()]
public void Relations__Var1NullEquals666_EqualsFalse()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("v1", null);
string expression = "v1 = 666";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.False((bool?)result);
}
[Fact()]
public void Relations__Var1NullEqualsStringHello_EqualsFalse()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("v1", null);
string expression = "v1 = \"Hello\"";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.False((bool?)result);
}
[Fact()]
public void Relations__Var1666Equals666_EqualsFalse()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("v1", 666);
string expression = "v1 = 666";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.True((bool?)result);
}
[Fact()]
public void Relations__Var1HelloEqualsStringHello_EqualsFalse()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("v1", "Hello");
string expression = "v1 = \"Hello\"";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.True((bool?)result);
}
#endregion Relations
#region BooleanOps
[TestMethod()]
[Fact]
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.False((bool?)Parser.EvaluateString("false and false"));
Assert.False((bool?)Parser.EvaluateString("false and true"));
Assert.False((bool?)Parser.EvaluateString("true and false"));
Assert.True((bool?)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"));
Assert.False((bool?)Parser.EvaluateString("False And False"));
Assert.False((bool?)Parser.EvaluateString("False And True"));
Assert.False((bool?)Parser.EvaluateString("True And False"));
Assert.True((bool?)Parser.EvaluateString("True And True"));
Assert.False((bool?)Parser.EvaluateString("false && false"));
Assert.False((bool?)Parser.EvaluateString("false && true"));
Assert.False((bool?)Parser.EvaluateString("true && false"));
Assert.True((bool?)Parser.EvaluateString("true && true"));
}
[TestMethod()]
[Fact]
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.False((bool?)Parser.EvaluateString("false or false"));
Assert.True((bool?)Parser.EvaluateString("false or true"));
Assert.True((bool?)Parser.EvaluateString("true or false"));
Assert.True((bool?)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"));
Assert.False((bool?)Parser.EvaluateString("False Or False"));
Assert.True((bool?)Parser.EvaluateString("False Or True"));
Assert.True((bool?)Parser.EvaluateString("True Or False"));
Assert.True((bool?)Parser.EvaluateString("True Or True"));
Assert.False((bool?)Parser.EvaluateString("false || false"));
Assert.True((bool?)Parser.EvaluateString("false || true"));
Assert.True((bool?)Parser.EvaluateString("true || false"));
Assert.True((bool?)Parser.EvaluateString("true || true"));
}
[TestMethod()]
[Fact]
public void BooleanOps_Not()
{
Assert.AreEqual(true, Parser.EvaluateString("!false"));
Assert.AreEqual(false, Parser.EvaluateString("!true"));
Assert.True((bool?)Parser.EvaluateString("!false"));
Assert.False((bool?)Parser.EvaluateString("!true"));
Assert.AreEqual(true, Parser.EvaluateString("not false"));
Assert.AreEqual(false, Parser.EvaluateString("not true"));
Assert.True((bool?)Parser.EvaluateString("not false"));
Assert.False((bool?)Parser.EvaluateString("not true"));
}
#endregion BooleanOps
#region Null value
[Fact]
public void NullValue_NullPlusAnything_EqualsNull()
{
Assert.Null((bool?)Parser.EvaluateString("null + 1"));
Assert.Null((bool?)Parser.EvaluateString("null + 100"));
Assert.Null((bool?)Parser.EvaluateString("1 + null"));
Assert.Null((bool?)Parser.EvaluateString("100 + null"));
Assert.Null((bool?)Parser.EvaluateString("null + null"));
}
[Fact]
public void NullValue_NullMinusAnything_EqualsNull()
{
Assert.Null((bool?)Parser.EvaluateString("null - 1"));
Assert.Null((bool?)Parser.EvaluateString("null - 100"));
Assert.Null((bool?)Parser.EvaluateString("1 - null"));
Assert.Null((bool?)Parser.EvaluateString("100 - null"));
Assert.Null((bool?)Parser.EvaluateString("null - null"));
}
[Fact]
public void NullValue_NullByAnything_EqualsNull()
{
Assert.Null((bool?)Parser.EvaluateString("null * 1"));
Assert.Null((bool?)Parser.EvaluateString("null * 100"));
Assert.Null((bool?)Parser.EvaluateString("1 * null"));
Assert.Null((bool?)Parser.EvaluateString("100 * null"));
Assert.Null((bool?)Parser.EvaluateString("null * null"));
Assert.Null((bool?)Parser.EvaluateString("null / 1"));
Assert.Null((bool?)Parser.EvaluateString("null / 100"));
Assert.Null((bool?)Parser.EvaluateString("1 / null"));
Assert.Null((bool?)Parser.EvaluateString("100 / null"));
Assert.Null((bool?)Parser.EvaluateString("null / null"));
}
#endregion
#region String coercions
[Fact()]
public void StringCoercions__Var1NullEqualsStringEmpty_EqualsTrue()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("v1", null);
string expression = "v1 = \"\"";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.True((bool?)result);
}
[Fact()]
public void StringCoercions__Var1TrueStringEqualsTrue_EqualsTrue()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("v1", "True");
string expression = "v1 = true";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.True((bool?)result);
}
[Fact()]
public void StringCoercions__Var1FalseStringEqualsFalse_EqualsTrue()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("v1", "False");
string expression = "v1 = false";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.True((bool?)result);
}
#endregion String coercions
#region Exceptions
[Fact]
public void Exceptions__HelloAtEnd__UnexpectedCharactersAtEndException()
{
try
{
string expression = "1 + 1 \"Hello\"";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedCharactersAtEndException>(ex);
}
}
[Fact]
public void Exceptions__TrueAtEndInsideParens__MissingCloseParenthesisException()
{
try
{
string expression = "(1+1 true)";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.MissingCloseParenthesisException>(ex);
}
}
[Fact]
public void Exceptions__TrueAtEndInsideFunctionCall__MissingCloseParenthesisException()
{
try
{
string expression = "Func(1 true)";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.MissingCloseParenthesisException>(ex);
}
}
[Fact]
public void Exceptions__EOF__UnexpectedEOFException()
{
try
{
string expression = "";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedEOFException>(ex);
}
}
[Fact]
public void Exceptions__Plus__UnexpectedEOFException()
{
try
{
string expression = "+";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedEOFException>(ex);
}
}
[Fact]
public void Exceptions__Minus__UnexpectedEOFException()
{
try
{
string expression = "-";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedEOFException>(ex);
}
}
[Fact]
public void Exceptions__OpenParens__UnexpectedEOFException()
{
try
{
string expression = "(";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedEOFException>(ex);
}
}
[Fact]
public void Exceptions__Comma__UnexpectedTokenException()
{
try
{
string expression = ",";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedTokenException>(ex);
}
}
[Fact]
public void Exceptions__Division__UnexpectedTokenException()
{
try
{
string expression = "/";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedTokenException>(ex);
}
}
[Fact]
public void Exceptions__Multiply__UnexpectedTokenException()
{
try
{
string expression = "*";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedTokenException>(ex);
}
}
[Fact]
public void Exceptions__CloseParens__UnexpectedTokenException()
{
try
{
string expression = ")";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedTokenException>(ex);
}
}
[Fact]
public void Exceptions__Parens__UnexpectedTokenException()
{
try
{
string expression = "()";
object result = Parser.EvaluateString(expression);
Assert.True(false);
}
catch (Exception ex)
{
Assert.IsType<Parser.UnexpectedTokenException>(ex);
}
}
#endregion Exceptions
#region Misc
[Fact()]
public void Misc__MixedExpression_EqualsFalse()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("QI_86", null);
evaluationContex.SetVariable("QI_87", null);
evaluationContex.SetVariable("QI_104", null);
string expression = "( QI_86 = 0 Or QI_86 = null ) And ( QI_87 = 0 Or QI_87 = null ) And QI_104 > 0";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.False((bool?)result);
}
[Fact()]
public void Misc__ProductWithStringDecimals_EqualsFalse()
{
EvaluationContext evaluationContex = new EvaluationContext();
evaluationContex.SetVariable("$1109", "1933");
evaluationContex.SetVariable("$1150", "02.00000");
string expression = "$1109 * $1150";
object result = Parser.EvaluateString(expression, evaluationContex);
Assert.Equal(3866.00000M, result);
}
#endregion Misc
}
}

View File

@@ -1,15 +0,0 @@
using System.Reflection;
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,225 +1,229 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using System.IO;
using Xunit;
namespace VAR.ExpressionEvaluator.Tests
{
[TestClass()]
public class TokenizerTests
{
[TestMethod()]
[Fact]
public void Tokenizer__Plus()
{
var testString = "10 + 20";
var t = new Tokenizer(new StringReader(testString));
// "10"
Assert.AreEqual(t.Token, Token.Number);
Assert.AreEqual(t.Number, 10);
Assert.Equal(Token.Number, t.Token);
Assert.Equal(10, t.Number);
t.NextToken();
// "+"
Assert.AreEqual(t.Token, Token.Plus);
Assert.Equal(Token.Plus, t.Token);
t.NextToken();
// "20"
Assert.AreEqual(t.Token, Token.Number);
Assert.AreEqual(t.Number, 20);
Assert.Equal(Token.Number, t.Token);
Assert.Equal(20, t.Number);
t.NextToken();
Assert.AreEqual(t.Token, Token.EOF);
Assert.Equal(Token.EOF, t.Token);
}
[TestMethod()]
[Fact]
public void Tokenizer__PlusMinusAndDecimal()
{
var testString = "10 + 20 - 30.123";
var t = new Tokenizer(new StringReader(testString));
// "10"
Assert.AreEqual(t.Token, Token.Number);
Assert.AreEqual(t.Number, 10);
Assert.Equal(Token.Number, t.Token);
Assert.Equal(10, t.Number);
t.NextToken();
// "+"
Assert.AreEqual(t.Token, Token.Plus);
Assert.Equal(Token.Plus, t.Token);
t.NextToken();
// "20"
Assert.AreEqual(t.Token, Token.Number);
Assert.AreEqual(t.Number, 20);
Assert.Equal(Token.Number, t.Token);
Assert.Equal(20, t.Number);
t.NextToken();
// "-"
Assert.AreEqual(t.Token, Token.Minus);
Assert.Equal(Token.Minus, t.Token);
t.NextToken();
// "20"
Assert.AreEqual(t.Token, Token.Number);
Assert.AreEqual(t.Number, 30.123m);
Assert.Equal(Token.Number, t.Token);
Assert.Equal(30.123m, t.Number);
t.NextToken();
Assert.AreEqual(t.Token, Token.EOF);
Assert.Equal(Token.EOF, t.Token);
}
[TestMethod()]
[Fact]
public void Tokenizer__SimpleString()
{
var testString = "\"Hello World\"";
var t = new Tokenizer(new StringReader(testString));
// "Hello World"
Assert.AreEqual(t.Token, Token.String);
Assert.AreEqual(t.Text, "Hello World");
Assert.Equal(Token.String, t.Token);
Assert.Equal("Hello World", t.Text);
t.NextToken();
Assert.AreEqual(t.Token, Token.EOF);
Assert.Equal(Token.EOF, t.Token);
}
[TestMethod()]
[Fact]
public void Tokenizer__StringWithEscaping()
{
var testString = "\"Hello \\\"World\\\"\"";
var t = new Tokenizer(new StringReader(testString));
// "Hello \"World\""
Assert.AreEqual(t.Token, Token.String);
Assert.AreEqual(t.Text, "Hello \"World\"");
Assert.Equal(Token.String, t.Token);
Assert.Equal("Hello \"World\"", t.Text);
t.NextToken();
Assert.AreEqual(t.Token, Token.EOF);
Assert.Equal(Token.EOF, t.Token);
}
[TestMethod()]
[Fact]
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"
Assert.AreEqual(t.Token, Token.Identifier);
Assert.AreEqual(t.Text, "null");
Assert.Equal(Token.Identifier, t.Token);
Assert.Equal("null", t.Text);
t.NextToken();
// "true"
Assert.AreEqual(t.Token, Token.Identifier);
Assert.AreEqual(t.Text, "true");
Assert.Equal(Token.Identifier, t.Token);
Assert.Equal("true", t.Text);
t.NextToken();
// "false"
Assert.AreEqual(t.Token, Token.Identifier);
Assert.AreEqual(t.Text, "false");
Assert.Equal(Token.Identifier, t.Token);
Assert.Equal("false", t.Text);
t.NextToken();
// "_$variable1"
Assert.AreEqual(t.Token, Token.Identifier);
Assert.AreEqual(t.Text, "_$variable1");
Assert.Equal(Token.Identifier, t.Token);
Assert.Equal("_$variable1", t.Text);
t.NextToken();
// "$variable2"
Assert.AreEqual(t.Token, Token.Identifier);
Assert.AreEqual(t.Text, "$variable2");
Assert.Equal(Token.Identifier, t.Token);
Assert.Equal("$variable2", t.Text);
t.NextToken();
Assert.AreEqual(t.Token, Token.EOF);
// ";{}#"
Assert.Equal(Token.Identifier, t.Token);
Assert.Equal(";{}#", t.Text);
t.NextToken();
Assert.Equal(Token.EOF, t.Token);
}
[TestMethod()]
[Fact]
public void Tokenizer__AllTogether()
{
var testString = "(10 + 20) * -30.123 + \"Hello \\\"World\\\"\" = false";
var t = new Tokenizer(new StringReader(testString));
// "("
Assert.AreEqual(t.Token, Token.ParenthesisStart);
Assert.Equal(Token.ParenthesisStart, t.Token);
t.NextToken();
// "10"
Assert.AreEqual(t.Token, Token.Number);
Assert.AreEqual(t.Number, 10);
Assert.Equal(Token.Number, t.Token);
Assert.Equal(10, t.Number);
t.NextToken();
// "+"
Assert.AreEqual(t.Token, Token.Plus);
Assert.Equal(Token.Plus, t.Token);
t.NextToken();
// "20"
Assert.AreEqual(t.Token, Token.Number);
Assert.AreEqual(t.Number, 20);
Assert.Equal(Token.Number, t.Token);
Assert.Equal(20, t.Number);
t.NextToken();
// ")"
Assert.AreEqual(t.Token, Token.ParenthesisEnd);
Assert.Equal(Token.ParenthesisEnd, t.Token);
t.NextToken();
// "*"
Assert.AreEqual(t.Token, Token.Multiply);
Assert.Equal(Token.Multiply, t.Token);
t.NextToken();
// "-"
Assert.AreEqual(t.Token, Token.Minus);
Assert.Equal(Token.Minus, t.Token);
t.NextToken();
// "20"
Assert.AreEqual(t.Token, Token.Number);
Assert.AreEqual(t.Number, 30.123m);
Assert.Equal(Token.Number, t.Token);
Assert.Equal(30.123m, t.Number);
t.NextToken();
// "+"
Assert.AreEqual(t.Token, Token.Plus);
Assert.Equal(Token.Plus, t.Token);
t.NextToken();
// "Hello \"World\""
Assert.AreEqual(t.Token, Token.String);
Assert.AreEqual(t.Text, "Hello \"World\"");
Assert.Equal(Token.String, t.Token);
Assert.Equal("Hello \"World\"", t.Text);
t.NextToken();
// "="
Assert.AreEqual(t.Token, Token.Equals);
Assert.Equal(Token.Equals, t.Token);
t.NextToken();
// "false"
Assert.AreEqual(t.Token, Token.Identifier);
Assert.AreEqual(t.Text, "false");
Assert.Equal(Token.Identifier, t.Token);
Assert.Equal("false", t.Text);
t.NextToken();
Assert.AreEqual(t.Token, Token.EOF);
Assert.Equal(Token.EOF, t.Token);
}
[TestMethod()]
[Fact]
public void Tokenizer__MoreTokens()
{
var testString = "= < > <= >= == === ";
var t = new Tokenizer(new StringReader(testString));
// "="
Assert.AreEqual(t.Token, Token.Equals);
Assert.Equal(Token.Equals, t.Token);
t.NextToken();
// "<"
Assert.AreEqual(t.Token, Token.LessThan);
Assert.Equal(Token.LessThan, t.Token);
t.NextToken();
// ">"
Assert.AreEqual(t.Token, Token.GreaterThan);
Assert.Equal(Token.GreaterThan, t.Token);
t.NextToken();
// "<="
Assert.AreEqual(t.Token, Token.LessOrEqualThan);
Assert.Equal(Token.LessOrEqualThan, t.Token);
t.NextToken();
// ">="
Assert.AreEqual(t.Token, Token.GreaterOrEqualThan);
Assert.Equal(Token.GreaterOrEqualThan, t.Token);
t.NextToken();
// "=="
Assert.AreEqual(t.Token, Token.Equals);
Assert.Equal(Token.Equals, t.Token);
t.NextToken();
// "==="
Assert.AreEqual(t.Token, Token.ExclusiveEquals);
Assert.Equal(Token.ExclusiveEquals, t.Token);
t.NextToken();
Assert.AreEqual(t.Token, Token.EOF);
Assert.Equal(Token.EOF, t.Token);
}
}

View File

@@ -1,94 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{3195F0E0-9489-49CE-8FC2-627A73F80CC7}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VAR.ExpressionEvaluator.Tests</RootNamespace>
<AssemblyName>VAR.ExpressionEvaluator.Tests</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<ProjectReference Include="..\VAR.ExpressionEvaluator\VAR.ExpressionEvaluator.csproj" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="ExpressionBinaryNodeTests.cs" />
<Compile Include="ExpressionNumberNodeTests.cs" />
<Compile Include="ExpressionStringNodeTests.cs" />
<Compile Include="ParserTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TokenizerTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VAR.ExpressionEvaluator\VAR.ExpressionEvaluator.csproj">
<Project>{74635F68-55B1-4819-84A3-9EA818D396D9}</Project>
<Name>VAR.ExpressionEvaluator</Name>
</ProjectReference>
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -1,11 +1,17 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.902
# Visual Studio Version 16
VisualStudioVersion = 16.0.31402.337
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.ExpressionEvaluator", "VAR.ExpressionEvaluator\VAR.ExpressionEvaluator.csproj", "{74635F68-55B1-4819-84A3-9EA818D396D9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VAR.ExpressionEvaluator.Tests", "VAR.ExpressionEvaluator.Tests\VAR.ExpressionEvaluator.Tests.csproj", "{3195F0E0-9489-49CE-8FC2-627A73F80CC7}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notes", "Notes", "{E4F0EB17-0866-42A7-A1BE-2357C3245C12}"
ProjectSection(SolutionItems) = preProject
LICENSE.txt = LICENSE.txt
README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VAR.ExpressionEvaluator.Tests", "VAR.ExpressionEvaluator.Tests\VAR.ExpressionEvaluator.Tests.csproj", "{89474ADF-B420-4774-BAC8-EFB3FBE28DA6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -17,10 +23,10 @@ Global
{74635F68-55B1-4819-84A3-9EA818D396D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74635F68-55B1-4819-84A3-9EA818D396D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74635F68-55B1-4819-84A3-9EA818D396D9}.Release|Any CPU.Build.0 = Release|Any CPU
{3195F0E0-9489-49CE-8FC2-627A73F80CC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3195F0E0-9489-49CE-8FC2-627A73F80CC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3195F0E0-9489-49CE-8FC2-627A73F80CC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3195F0E0-9489-49CE-8FC2-627A73F80CC7}.Release|Any CPU.Build.0 = Release|Any CPU
{89474ADF-B420-4774-BAC8-EFB3FBE28DA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89474ADF-B420-4774-BAC8-EFB3FBE28DA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89474ADF-B420-4774-BAC8-EFB3FBE28DA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89474ADF-B420-4774-BAC8-EFB3FBE28DA6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,25 +0,0 @@
@echo off
:: MSBuild and tools path
if exist "%windir%\Microsoft.Net\Framework\v4.0.30319" set MsBuildPath=%windir%\Microsoft.NET\Framework\v4.0.30319
if exist "%windir%\Microsoft.Net\Framework64\v4.0.30319" set MsBuildPath=%windir%\Microsoft.NET\Framework64\v4.0.30319
if exist "C:\Program Files (x86)\MSBuild\14.0\Bin" set MsBuildPath=C:\Program Files (x86)\MSBuild\14.0\Bin
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin" set MsBuildPath=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin
set PATH=%MsBuildPath%;%PATH%
echo %MsBuildPath%
:: NuGet
set nuget="nuget"
if exist "%~dp0\..\packages\NuGet.CommandLine.5.3.1\tools\NuGet.exe" set nuget="%~dp0\..\packages\NuGet.CommandLine.5.3.1\tools\NuGet.exe"
:: Release
Title Building Release
msbuild VAR.ExpressionEvaluator.csproj /t:Build /p:Configuration="Release" /p:Platform="AnyCPU"
:: Packing Nuget
Title Packing Nuget
%nuget% pack VAR.ExpressionEvaluator.csproj -Verbosity detailed -OutputDir "NuGet" -Properties Configuration="Release" -Prop Platform=AnyCPU
title Finished
pause

View File

@@ -39,11 +39,16 @@ 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");
}
if ((value is string) == false && (value is bool) == false)
if ((value is string) == false && (value is bool) == false && value != null)
{
value = Convert.ToDecimal(value);
}
@@ -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,9 +11,14 @@ 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)
if (decimal.TryParse((string)leftValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue));
}
@@ -21,7 +26,7 @@ namespace VAR.ExpressionEvaluator
}
if (rightValue is string)
{
if (decimal.TryParse((string)rightValue, out decimal dec) == false)
if (decimal.TryParse((string)rightValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue));
}

View File

@@ -1,6 +1,4 @@
using System;
namespace VAR.ExpressionEvaluator
namespace VAR.ExpressionEvaluator
{
public class ExpressionEqualsNode : ExpressionBinaryNode
{
@@ -9,35 +7,105 @@ namespace VAR.ExpressionEvaluator
{
}
private static object EqualsOp(object leftValue, object rightValue)
public static object EqualsOp(object leftValue, object rightValue)
{
// Null
if (leftValue is string && string.IsNullOrEmpty(leftValue as string))
{
leftValue = null;
}
if (rightValue is string && string.IsNullOrEmpty(rightValue as string))
{
rightValue = null;
}
if (leftValue == null && rightValue == null)
{
return true;
}
if (leftValue == null || rightValue == null)
{
return false;
}
// String
if (leftValue is string && rightValue is string)
{
return string.Compare((string)leftValue, (string)rightValue) == 0;
}
// Bool
if (leftValue is bool || rightValue is bool)
{
bool? leftBool = ConvertToNullableBool(leftValue);
bool? rightBool = ConvertToNullableBool(rightValue);
return leftBool == rightBool;
}
// Decimal
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)
if (decimal.TryParse((string)leftValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue));
leftValue = null;
}
else
{
leftValue = dec;
}
}
if (rightValue is string)
{
if (decimal.TryParse((string)rightValue, out decimal dec) == false)
if (decimal.TryParse((string)rightValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue));
rightValue = null;
}
else
{
rightValue = dec;
}
}
if ((leftValue is decimal) == false || (rightValue is decimal) == false)
{
throw new Exception("Can't compare non decimal values");
return false;
}
return (decimal)leftValue == (decimal)rightValue;
}
private static bool? ConvertToNullableBool(object value)
{
if (value is bool)
{
return (bool)value;
}
if (value is string)
{
string text = value as string;
if (string.IsNullOrEmpty(text))
{
return null;
}
decimal decValue;
if (decimal.TryParse(text, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decValue))
{
return decValue != 0;
}
string textLower = text.ToLower();
if (textLower == "false" || textLower == "no" || textLower == "ez" || textLower == "non" || textLower == "nein")
{
return false;
}
if (textLower == "true" || textLower == "si" || textLower == "sí" || textLower == "yes" || textLower == "bai" || textLower == "oui" || textLower == "ja")
{
return true;
}
return null;
}
if (value is decimal)
{
return ((decimal)value) != 0;
}
return false;
}
}
}

View File

@@ -18,20 +18,39 @@ namespace VAR.ExpressionEvaluator
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)
if (string.IsNullOrEmpty((string)leftValue))
{
leftValue = null;
}
else
{
if (decimal.TryParse((string)leftValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, 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)
if (string.IsNullOrEmpty((string)rightValue))
{
leftValue = null;
}
else
{
if (decimal.TryParse((string)rightValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue));
}
rightValue = dec;
}
}
if (leftValue == null || rightValue == null)
{
return false;
}
if ((leftValue is decimal) == false || (rightValue is decimal) == false)
{

View File

@@ -18,20 +18,39 @@ namespace VAR.ExpressionEvaluator
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)
if (string.IsNullOrEmpty((string)leftValue))
{
leftValue = null;
}
else
{
if (decimal.TryParse((string)leftValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, 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)
if (string.IsNullOrEmpty((string)rightValue))
{
leftValue = null;
}
else
{
if (decimal.TryParse((string)rightValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue));
}
rightValue = dec;
}
}
if (leftValue == null || rightValue == null)
{
return false;
}
if ((leftValue is decimal) == false || (rightValue is decimal) == false)
{

View File

@@ -18,20 +18,39 @@ namespace VAR.ExpressionEvaluator
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)
if (string.IsNullOrEmpty((string)leftValue))
{
leftValue = null;
}
else
{
if (decimal.TryParse((string)leftValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, 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)
if (string.IsNullOrEmpty((string)rightValue))
{
leftValue = null;
}
else
{
if (decimal.TryParse((string)rightValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue));
}
rightValue = dec;
}
}
if (leftValue == null || rightValue == null)
{
return false;
}
if ((leftValue is decimal) == false || (rightValue is decimal) == false)
{

View File

@@ -18,20 +18,39 @@ namespace VAR.ExpressionEvaluator
if (leftValue is string)
{
if (decimal.TryParse((string)leftValue, out decimal dec) == false)
if (string.IsNullOrEmpty((string)leftValue))
{
leftValue = null;
}
else
{
if (decimal.TryParse((string)leftValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, 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)
if (string.IsNullOrEmpty((string)rightValue))
{
leftValue = null;
}
else
{
if (decimal.TryParse((string)rightValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue));
}
rightValue = dec;
}
}
if (leftValue == null || rightValue == null)
{
return false;
}
if ((leftValue is decimal) == false || (rightValue is decimal) == false)
{

View File

@@ -11,9 +11,14 @@ 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)
if (decimal.TryParse((string)leftValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue));
}
@@ -21,7 +26,7 @@ namespace VAR.ExpressionEvaluator
}
if (rightValue is string)
{
if (decimal.TryParse((string)rightValue, out decimal dec) == false)
if (decimal.TryParse((string)rightValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue));
}

View File

@@ -11,9 +11,14 @@ 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)
if (decimal.TryParse((string)leftValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)leftValue));
}
@@ -21,7 +26,7 @@ namespace VAR.ExpressionEvaluator
}
if (rightValue is string)
{
if (decimal.TryParse((string)rightValue, out decimal dec) == false)
if (decimal.TryParse((string)rightValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)rightValue));
}

View File

@@ -1,6 +1,4 @@
using System;
namespace VAR.ExpressionEvaluator
namespace VAR.ExpressionEvaluator
{
public class ExpressionNotEqualsNode : ExpressionBinaryNode
{
@@ -11,33 +9,8 @@ namespace VAR.ExpressionEvaluator
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;
bool result = (bool)ExpressionEqualsNode.EqualsOp(leftValue, rightValue);
return !result;
}
}
}

View File

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

View File

@@ -11,9 +11,14 @@ 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)
if (decimal.TryParse((string)value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out decimal dec) == false)
{
throw new Exception(string.Format("Can't convert to decimal string value \"{0}\"", (string)value));
}

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

@@ -6,24 +6,42 @@ namespace VAR.ExpressionEvaluator
{
public class Parser
{
private ITokenizer _tokenizer;
#region Custom exceptions
public class UnexpectedCharactersAtEndException : Exception
{
public UnexpectedCharactersAtEndException() : base("Unexpected characters at end of expression") { }
}
public class MissingCloseParenthesisException : Exception
{
public MissingCloseParenthesisException() : base("Missing close parenthesis") { }
}
public class UnexpectedEOFException : Exception
{
public UnexpectedEOFException() : base("Unexpected EOF") { }
}
public class UnexpectedTokenException : Exception
{
public UnexpectedTokenException(string token) : base(string.Format("Unexpected token: {0}", token)) { }
}
#endregion Custom exceptions
#region Creator
private readonly ITokenizer _tokenizer;
public Parser(ITokenizer tokenizer)
{
_tokenizer = tokenizer;
}
public IExpressionNode ParseExpression()
{
var expr = ParseBooleanOp();
#endregion Creator
if (_tokenizer.Token != Token.EOF)
{
throw new Exception("Unexpected characters at end of expression");
}
return expr;
}
#region Parsing methods
private IExpressionNode ParseBooleanOp()
{
@@ -42,7 +60,7 @@ namespace VAR.ExpressionEvaluator
IExpressionNode rightNode = ParseRelations();
leftNode = new ExpressionBooleanAndNode(leftNode, rightNode);
}
if (_tokenizer.Token == Token.Or)
else if (_tokenizer.Token == Token.Or)
{
_tokenizer.NextToken();
IExpressionNode rightNode = ParseRelations();
@@ -66,6 +84,13 @@ namespace VAR.ExpressionEvaluator
IExpressionNode rightNode = ParsePlusAndMinus();
leftNode = new ExpressionEqualsNode(leftNode, rightNode);
}
else if (_tokenizer.Token == Token.ExclusiveEquals)
{
// TODO: Implement ExpressionExclusiveEqualsNode
_tokenizer.NextToken();
IExpressionNode rightNode = ParsePlusAndMinus();
leftNode = new ExpressionEqualsNode(leftNode, rightNode);
}
else if (_tokenizer.Token == Token.NotEquals)
{
_tokenizer.NextToken();
@@ -197,6 +222,11 @@ namespace VAR.ExpressionEvaluator
_tokenizer.NextToken();
return new ExpressionBooleanNode(false);
}
if (identifierToLower == "null")
{
_tokenizer.NextToken();
return new ExpressionNullNode();
}
string identifier = _tokenizer.Text;
_tokenizer.NextToken();
@@ -209,9 +239,11 @@ namespace VAR.ExpressionEvaluator
{
_tokenizer.NextToken();
var parameters = new List<IExpressionNode>();
if (_tokenizer.Token != Token.ParenthesisEnd)
{
while (true)
{
parameters.Add(ParsePlusAndMinus());
parameters.Add(ParseBooleanOp());
if (_tokenizer.Token == Token.Comma)
{
_tokenizer.NextToken();
@@ -219,9 +251,10 @@ namespace VAR.ExpressionEvaluator
}
break;
}
}
if (_tokenizer.Token != Token.ParenthesisEnd)
{
throw new Exception("Missing close parenthesis");
throw new MissingCloseParenthesisException();
}
_tokenizer.NextToken();
@@ -236,13 +269,34 @@ namespace VAR.ExpressionEvaluator
IExpressionNode node = ParseBooleanOp();
if (_tokenizer.Token != Token.ParenthesisEnd)
{
throw new Exception("Missing close parenthesis");
throw new MissingCloseParenthesisException();
}
_tokenizer.NextToken();
return node;
}
throw new Exception(string.Format("Unexpected token: {0}", _tokenizer.Token.ToString()));
if (_tokenizer.Token == Token.EOF)
{
throw new UnexpectedEOFException();
}
throw new UnexpectedTokenException(_tokenizer.Token.ToString());
}
#endregion Parsing methods
#region Public API
public IExpressionNode ParseExpression()
{
var expr = ParseBooleanOp();
if (_tokenizer.Token != Token.EOF)
{
throw new UnexpectedCharactersAtEndException();
}
return expr;
}
public static IExpressionNode ParseString(string str)
@@ -258,5 +312,7 @@ namespace VAR.ExpressionEvaluator
IExpressionNode node = ParseString(str);
return node.Eval(evaluationContext);
}
#endregion Public API
}
}

View File

@@ -1,15 +0,0 @@
using System.Reflection;
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

@@ -144,6 +144,11 @@ namespace VAR.ExpressionEvaluator
NextChar();
_currentToken = Token.LessOrEqualThan;
}
if (_currentChar == '>')
{
NextChar();
_currentToken = Token.NotEquals;
}
return;
case '&':
@@ -181,6 +186,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")
{
@@ -257,6 +297,12 @@ namespace VAR.ExpressionEvaluator
haveDecimalPoint = _currentChar == '.';
NextChar();
}
if (haveDecimalPoint && sbNumber.Length == 1)
{
_text = sbNumber.ToString();
_currentToken = Token.String;
return;
}
_number = decimal.Parse(sbNumber.ToString(), CultureInfo.InvariantCulture);
_currentToken = Token.Number;
return;

View File

@@ -1,80 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{74635F68-55B1-4819-84A3-9EA818D396D9}</ProjectGuid>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VAR.ExpressionEvaluator</RootNamespace>
<AssemblyName>VAR.ExpressionEvaluator</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<IsPackable>true</IsPackable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PropertyGroup>
<PackageId>VAR.ExpressionEvaluator</PackageId>
<Title>VAR.ExpressionEvaluator</Title>
<Version>0.2.8</Version>
<Description>Expression Evaluation Library</Description>
<Authors>VAR</Authors>
<Company>VAR</Company>
<Copyright>Copyright © VAR 2019-2021</Copyright>
<RequireLicenseAcceptance>false</RequireLicenseAcceptance>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageProjectUrl>https://github.com/Kableado/VAR.ExpressionEvaluator</PackageProjectUrl>
<PackageTags>Expression;Evaluation;Expression Evaluation</PackageTags>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Content Include="..\LICENSE.txt" Link="LICENSE.txt" Pack="true" PackagePath="" />
</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" />
<Compile Include="ExpressionNodes\ExpressionGreaterOrEqualThanNode.cs" />
<Compile Include="ExpressionNodes\ExpressionGreaterThanNode.cs" />
<Compile Include="ExpressionNodes\ExpressionLessOrEqualThanNode.cs" />
<Compile Include="ExpressionNodes\ExpressionLessThanNode.cs" />
<Compile Include="ExpressionNodes\ExpressionMultiplyNode.cs" />
<Compile Include="ExpressionNodes\ExpressionNotEqualsNode.cs" />
<Compile Include="ExpressionNodes\ExpressionUnaryNode.cs" />
<Compile Include="ExpressionNodes\ExpressionBinaryNode.cs" />
<Compile Include="ExpressionNodes\ExpressionMinusNode.cs" />
<Compile Include="ExpressionNodes\ExpressionNumberNegateNode.cs" />
<Compile Include="ExpressionNodes\ExpressionPlusNode.cs" />
<Compile Include="ExpressionNodes\ExpressionStringNode.cs" />
<Compile Include="ExpressionNodes\ExpressionNumberNode.cs" />
<Compile Include="ExpressionNodes\ExpressionVariableNode.cs" />
<Compile Include="IEvaluationContext.cs" />
<Compile Include="ExpressionNodes\IExpressionNode.cs" />
<Compile Include="ITokenizer.cs" />
<Compile Include="Parser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Token.cs" />
<Compile Include="Tokenizer.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Build.NuGet.cmd" />
<None Include="packages.config" />
<None Include="VAR.ExpressionEvaluator.nuspec" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="CopyPackage" AfterTargets="Pack">
<Copy SourceFiles="$(OutputPath)..\$(PackageId).$(PackageVersion).nupkg" DestinationFolder="Nuget\" />
</Target>
</Project>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<authors>$author$</authors>
<owners>$author$</owners>
<licenseUrl>https://varstudio.net/VAR.ExpressionEvaluator/LICENSE.txt</licenseUrl>
<projectUrl>https://varstudio.net/VAR.ExpressionEvaluator</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<copyright>Copyright VAR 2019</copyright>
<tags>Expression Evaluation</tags>
</metadata>
<files>
<file src="bin\Release\VAR.ExpressionEvaluator.dll" target="lib\net461\VAR.ExpressionEvaluator.dll" />
<file src="bin\Release\VAR.ExpressionEvaluator.pdb" target="lib\net461\VAR.ExpressionEvaluator.pdb" />
</files>
</package>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NuGet.CommandLine" version="5.3.1" targetFramework="net461" developmentDependency="true" />
</packages>