├── .github └── workflows │ ├── BuildAndTest.yml │ └── Deploy.yml ├── .gitignore ├── README.md ├── license.txt └── src ├── Dahomey.ExpressionEvaluator.Tests ├── Dahomey.ExpressionEvaluator.Tests.csproj ├── ExpressionParserTest.cs ├── ExpressionTest.cs └── ReflectionHelperTest.cs ├── Dahomey.ExpressionEvaluator.sln └── Dahomey.ExpressionEvaluator ├── Dahomey.ExpressionEvaluator.csproj ├── ExpressionLexer.cs ├── ExpressionParser.cs ├── Expressions ├── BooleanLiteralExpression.cs ├── BooleanLogicalExpression.cs ├── EnumLiteralExpression.cs ├── IBooleanExpression.cs ├── IExpression.cs ├── INumericExpression.cs ├── IObjectExpression.cs ├── IStringExpression.cs ├── ListExpression.cs ├── NumericArithmeticExpression.cs ├── NumericComparisonExpression.cs ├── NumericConditionalExpression.cs ├── NumericFuncExpression.cs ├── NumericListElementExpression.cs ├── NumericLiteralExpression.cs ├── NumericMethodExpression.cs ├── NumericPropertyExpression.cs ├── NumericVariableExpression.cs ├── ObjectFuncExpression.cs ├── ObjectListElementExpression.cs ├── ObjectMethodExpression.cs ├── ObjectPropertyExpression.cs ├── ObjectVariableExpression.cs ├── StringComparisonExpression.cs └── StringLiteralExpression.cs ├── Operator.cs ├── ReflectionHelper.cs ├── SyntaxErrorException.cs └── grammar.txt /.github/workflows/BuildAndTest.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'src/**' 7 | - '.github/workflows/**' 8 | branches: 9 | - '**' 10 | tags-ignore: 11 | - '*.*.*' 12 | pull_request: 13 | paths: 14 | - 'src/**' 15 | - '.github/workflows/**' 16 | branches: 17 | - '**' 18 | tags-ignore: 19 | - '*.*.*' 20 | jobs: 21 | build: 22 | 23 | runs-on: ubuntu-latest 24 | 25 | steps: 26 | - name: checkout 27 | uses: actions/checkout@v1 28 | 29 | - name: setup 30 | uses: actions/setup-dotnet@v1 31 | with: 32 | dotnet-version: 2.2.108 33 | 34 | - name: build 35 | run: dotnet build -c Release src/Dahomey.ExpressionEvaluator 36 | 37 | - name: test 38 | run: dotnet test -c Release src/Dahomey.ExpressionEvaluator.Tests 39 | -------------------------------------------------------------------------------- /.github/workflows/Deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to Nuget 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: checkout 14 | uses: actions/checkout@v1 15 | 16 | - name: setup 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: 3.1.100 20 | 21 | - name: build 22 | run: dotnet pack -p:Version=${GITHUB_REF##*/} -p:FileVersion=${GITHUB_REF##*/} -p:AssemblyVersion=${GITHUB_REF##*/} -c Release src/Dahomey.ExpressionEvaluator 23 | 24 | - name: deploy 25 | run: dotnet nuget push src/Dahomey.ExpressionEvaluator/bin/Release/Dahomey.ExpressionEvaluator.${GITHUB_REF##*/}.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | .vs 5 | _ReSharper*/ 6 | *.csproj.ReSharper 7 | 8 | # Build output 9 | src/**/obj/ 10 | src/**/bin/ 11 | project.lock.json 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dahomey.ExpressionEvaluator 2 | Evaluate C# Formulas at Runtime 3 | 4 | ## Supported Platforms 5 | * .Net Standard 2.0. Compatible with 6 | * .Net Core 2.0+ 7 | * .Net Framework 4.6.1+ 8 | * Unity 2018.1+ 9 | 10 | Dahomey.ExpressionEvaluator code does not trigger any AOT complilation. It means it can be used safely with Unity IL2CPP. 11 | 12 | ## Installation 13 | ### NuGet 14 | https://www.nuget.org/packages/Dahomey.ExpressionEvaluator/ 15 | 16 | `Install-Package Dahomey.ExpressionEvaluator` 17 | 18 | ### Compilation from source 19 | 1. `dotnet restore` 20 | 2. `dotnet pack -c Release` 21 | 22 | ## Examples 23 | ### Parse a numeric expression 24 | ```csharp 25 | ExpressionParser parser = new ExpressionParser(); 26 | parser.RegisterVariable("a"); 27 | INumericExpression expr = parser.ParseNumericExpression("1 + a"); 28 | 29 | int a = 2; 30 | double result = expr.Evaluate(new Dictionary { { "a", a } }); 31 | Console.WriteLine(result); 32 | ``` 33 | The result will be: 34 | ```csharp 35 | 3 36 | ``` 37 | 38 | ### Parse a numeric expression with member access 39 | ```csharp 40 | class A 41 | { 42 | public B B { get; set; } 43 | } 44 | 45 | class B 46 | { 47 | public int Id { get; set; } 48 | } 49 | 50 | ExpressionParser parser = new ExpressionParser(); 51 | parser.RegisterVariable("a"); 52 | INumericExpression expr = parser.ParseNumericExpression("1 + a.B.Id"); 53 | 54 | A a = new A { B = new B { Id = 12 } }; 55 | double result = expr.Evaluate(new Dictionary { { "a", a } }); 56 | Console.WriteLine(result); 57 | ``` 58 | The result will be: 59 | ```csharp 60 | 13 61 | ``` 62 | 63 | ### Parse a numeric expression with array or list access 64 | ```csharp 65 | ExpressionParser parser = new ExpressionParser(); 66 | parser.RegisterVariable>("a"); 67 | INumericExpression expr = parser.ParseNumericExpression("1 + a[1]"); 68 | 69 | List a = new List { 1, 2 }; 70 | double result = expr.Evaluate(new Dictionary { { "a", a } }); 71 | Console.WriteLine(result); 72 | ``` 73 | The result will be: 74 | ```csharp 75 | 3 76 | ``` 77 | 78 | ### Parse a numeric expression with function access 79 | ```csharp 80 | Func func = n => Math.Cos(n); 81 | ExpressionParser parser = new ExpressionParser(); 82 | parser.RegisterFunction("cos", func); 83 | INumericExpression expr = parser.ParseNumericExpression("1 + cos(12)"); 84 | 85 | double result = expr.Evaluate() 86 | Console.WriteLine(result); 87 | ``` 88 | The result will be: 89 | ```csharp 90 | 1.8438539587324922 91 | ``` 92 | 93 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2017, Dahomey Technologies and Contributors 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | Except as contained in this notice, the name of Dahomey Technologies shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Dahomey Technologies. 26 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator.Tests/Dahomey.ExpressionEvaluator.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator.Tests/ExpressionParserTest.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Reflection; 12 | using Xunit; 13 | 14 | namespace Dahomey.ExpressionEvaluator.Tests 15 | { 16 | public class ExpressionParserTest 17 | { 18 | class A 19 | { 20 | public B B { get; set; } 21 | public List Bs { get; set; } 22 | public int f(byte i, string str, bool b) { return 12 + i; } 23 | public B g(short i, string str, bool b) { return Bs[i]; } 24 | } 25 | 26 | class B 27 | { 28 | public int Id { get; set; } 29 | } 30 | 31 | public enum Color 32 | { 33 | Red = 1, 34 | Green = 2, 35 | Blue = 3 36 | } 37 | 38 | #if NET35 39 | [Fact] 40 | public void NumericExpressionTheory() 41 | { 42 | NumericExpressionTest("1+2*3", "1 2 3 * +", 7); 43 | NumericExpressionTest("(1+2)*-3", "1 2 + 3 - *", (1 + 2) * -3); 44 | NumericExpressionTest("(1+2)*(3+4)", "1 2 + 3 4 + *", (1 + 2) * (3 + 4)); 45 | NumericExpressionTest("1+2+3", "1 2 + 3 +", 1 + 2 + 3); 46 | NumericExpressionTest("1+2", "1 2 +", 1 + 2); 47 | NumericExpressionTest("1 + Color.Green)", "1 Color.Green +", 1 + (int)Color.Green); 48 | } 49 | #else 50 | [Theory] 51 | [InlineData("1+2*3", "1 2 3 * +", 7)] 52 | [InlineData("(1+2)*-3", "1 2 + 3 - *", (1 + 2) * -3)] 53 | [InlineData("(1+2)*(3+4)", "1 2 + 3 4 + *", (1 + 2) * (3 + 4))] 54 | [InlineData("1+2+3", "1 2 + 3 +", 1 + 2 + 3)] 55 | [InlineData("1+2", "1 2 +", 1 + 2)] 56 | [InlineData("1 + Color.Green)", "1 Color.Green +", 1 + Color.Green)] 57 | #endif 58 | public void NumericExpressionTest(string expression, string expectedRpn, double expectedValue) 59 | { 60 | ExpressionParser parser = new ExpressionParser(); 61 | parser.RegisterAssembly(GetType().Assembly); 62 | INumericExpression expr = parser.ParseNumericExpression(expression); 63 | Assert.NotNull(expr); 64 | Assert.Equal(expectedRpn, expr.ToString()); 65 | Assert.Equal(expectedValue, expr.Evaluate()); 66 | } 67 | 68 | #if NET35 69 | [Fact] 70 | public void BooleanExpressionTheory() 71 | { 72 | BooleanExpressionTest("(1+2) < (3+4)", "1 2 + 3 4 + <", true); 73 | BooleanExpressionTest("(1+2) <= (3+4)", "1 2 + 3 4 + <=", true); 74 | BooleanExpressionTest("(1+2) == (3+4)", "1 2 + 3 4 + ==", false); 75 | BooleanExpressionTest("true", "true", true); 76 | BooleanExpressionTest("false", "false", false); 77 | BooleanExpressionTest("1 < 2 || 1 > 2", "1 2 < 1 2 > ||", true); 78 | BooleanExpressionTest("1 < 2 && 1 > 2", "1 2 < 1 2 > &&", false); 79 | BooleanExpressionTest("!(1 > 2)", "1 2 > !", true); 80 | } 81 | #else 82 | [Theory] 83 | [InlineData("(1+2) < (3+4)", "1 2 + 3 4 + <", true)] 84 | [InlineData("(1+2) <= (3+4)", "1 2 + 3 4 + <=", true)] 85 | [InlineData("(1+2) == (3+4)", "1 2 + 3 4 + ==", false)] 86 | [InlineData("true", "true", true)] 87 | [InlineData("false", "false", false)] 88 | [InlineData("1 < 2 || 1 > 2", "1 2 < 1 2 > ||", true)] 89 | [InlineData("1 < 2 && 1 > 2", "1 2 < 1 2 > &&", false)] 90 | [InlineData("!(1 > 2)", "1 2 > !", true)] 91 | #endif 92 | public void BooleanExpressionTest(string expression, string expectedRpn, bool expectedValue) 93 | { 94 | IBooleanExpression expr = new ExpressionParser().ParseBooleanExpression(expression); 95 | Assert.NotNull(expr); 96 | Assert.Equal(expectedRpn, expr.ToString()); 97 | Assert.Equal(expectedValue, expr.Evaluate()); 98 | } 99 | 100 | [Fact] 101 | public void NumericVariableTest() 102 | { 103 | ExpressionParser parser = new ExpressionParser(); 104 | parser.RegisterVariable("a"); 105 | 106 | int a = 2; 107 | INumericExpression expr = parser.ParseNumericExpression("1 + a"); 108 | 109 | Assert.NotNull(expr); 110 | Assert.Equal("1 a +", expr.ToString()); 111 | Assert.Equal(1 + a, expr.Evaluate(new Dictionary { { "a", a } })); 112 | } 113 | 114 | [Fact] 115 | public void NumericListVariableTest() 116 | { 117 | ExpressionParser parser = new ExpressionParser(); 118 | parser.RegisterVariable>("a"); 119 | 120 | List a = new List { 1, 2 }; 121 | INumericExpression expr = parser.ParseNumericExpression("1 + a[1]"); 122 | 123 | Assert.NotNull(expr); 124 | Assert.Equal("1 a[1] +", expr.ToString()); 125 | Assert.Equal(1 + a[1], expr.Evaluate(new Dictionary { { "a", a } })); 126 | } 127 | 128 | [Fact] 129 | public void NumericArrayVariableTest() 130 | { 131 | ExpressionParser parser = new ExpressionParser(); 132 | parser.RegisterVariable("a"); 133 | 134 | int[] a = new[] { 1, 2 }; 135 | INumericExpression expr = parser.ParseNumericExpression("1 + a[1]"); 136 | 137 | Assert.NotNull(expr); 138 | Assert.Equal("1 a[1] +", expr.ToString()); 139 | Assert.Equal(1 + a[1], expr.Evaluate(new Dictionary { { "a", a } })); 140 | } 141 | 142 | [Fact] 143 | public void MemberAccessExpressionTest() 144 | { 145 | ExpressionParser parser = new ExpressionParser(); 146 | parser.RegisterVariable("a"); 147 | 148 | INumericExpression expr = parser.ParseNumericExpression("1 + a.B.Id"); 149 | 150 | Assert.NotNull(expr); 151 | Assert.Equal("1 a.B.Id +", expr.ToString()); 152 | 153 | A a = new A { B = new B { Id = 12 } }; 154 | Assert.Equal(13, expr.Evaluate(new Dictionary { { "a", a } })); 155 | } 156 | 157 | [Fact] 158 | public void NumericFunctionTest() 159 | { 160 | Func func = n => Math.Cos(n); 161 | ExpressionParser parser = new ExpressionParser(); 162 | parser.RegisterFunction("cos", func); 163 | 164 | INumericExpression expr = parser.ParseNumericExpression("1 + cos(12)"); 165 | 166 | Assert.NotNull(expr); 167 | Assert.Equal("1 cos(12) +", expr.ToString()); 168 | Assert.Equal(1 + Math.Cos(12), expr.Evaluate()); 169 | } 170 | 171 | [Fact] 172 | public void ObjectFunctionTest() 173 | { 174 | B[] items = new[] 175 | { 176 | new B { Id = 12 }, 177 | new B { Id = 13 } 178 | }; 179 | 180 | Func func = n => items[n]; 181 | ExpressionParser parser = new ExpressionParser(); 182 | parser.RegisterFunction("item", func); 183 | 184 | INumericExpression expr = parser.ParseNumericExpression("1 + item(1).Id"); 185 | 186 | Assert.NotNull(expr); 187 | Assert.Equal("1 item(1).Id +", expr.ToString()); 188 | Assert.Equal(1 + items[1].Id, expr.Evaluate()); 189 | } 190 | 191 | [Fact] 192 | public void NumericMethodFunctionTest() 193 | { 194 | ExpressionParser parser = new ExpressionParser(); 195 | parser.RegisterVariable("a", typeof(A)); 196 | 197 | INumericExpression expr = parser.ParseNumericExpression("1 + a.f(2,\"foo\",true)"); 198 | 199 | Assert.NotNull(expr); 200 | Assert.Equal("1 a.f(2,\"foo\",true) +", expr.ToString()); 201 | 202 | A a = new A(); 203 | Assert.Equal(1 + a.f(2, "foo", true), expr.Evaluate(new Dictionary { { "a", a } })); 204 | } 205 | 206 | [Fact] 207 | public void ObjectMethodFunctionTest() 208 | { 209 | ExpressionParser parser = new ExpressionParser(); 210 | parser.RegisterVariable("a", typeof(A)); 211 | 212 | INumericExpression expr = parser.ParseNumericExpression("1 + a.g(2,\"foo\",true).Id"); 213 | 214 | Assert.NotNull(expr); 215 | Assert.Equal("1 a.g(2,\"foo\",true).Id +", expr.ToString()); 216 | 217 | A a = new A 218 | { 219 | Bs = new List 220 | { 221 | new B { Id = 1 }, 222 | new B { Id = 2 }, 223 | new B { Id = 3 }, 224 | } 225 | }; 226 | Assert.Equal(1 + a.g(2, "foo", true).Id, expr.Evaluate(new Dictionary { { "a", a } })); 227 | } 228 | 229 | public class Item 230 | { 231 | public Color Color { get; set; } 232 | } 233 | 234 | [Fact] 235 | public void EnumFuncParam() 236 | { 237 | ExpressionParser parser = new ExpressionParser(); 238 | parser.RegisterAssembly(GetType().Assembly); 239 | 240 | Func colorFunc = c => c; 241 | parser.RegisterFunction("color", colorFunc); 242 | 243 | INumericExpression expr = parser.ParseNumericExpression("color(Color.Green)"); 244 | Assert.NotNull(expr); 245 | Assert.Equal("color(Color.Green)", expr.ToString()); 246 | Assert.Equal((int)Color.Green, expr.Evaluate()); 247 | } 248 | 249 | [Fact] 250 | public void AdvancedEnumTest() 251 | { 252 | ExpressionParser parser = new ExpressionParser(); 253 | parser.RegisterAssembly(GetType().Assembly); 254 | 255 | List items = new List 256 | { 257 | new Item { Color = Color.Red }, 258 | new Item { Color = Color.Green }, 259 | new Item { Color = Color.Blue }, 260 | }; 261 | 262 | Func itemFunc = i => items[i]; 263 | parser.RegisterFunction("item", itemFunc); 264 | 265 | IBooleanExpression expr = parser.ParseBooleanExpression("item(1).Color == Color.Green"); 266 | Assert.NotNull(expr); 267 | Assert.Equal("item(1).Color Color.Green ==", expr.ToString()); 268 | Assert.True(expr.Evaluate()); 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator.Tests/ExpressionTest.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System.Collections.Generic; 10 | using Xunit; 11 | 12 | namespace Dahomey.ExpressionEvaluator.Tests 13 | { 14 | public class A 15 | { 16 | public B[] Bs { get; set; } 17 | public List Ints { get; set; } 18 | } 19 | 20 | public class B 21 | { 22 | public int Id { get; set; } 23 | } 24 | 25 | public class ExpressionTest 26 | { 27 | [Fact] 28 | public void ObjectListElementExpression() 29 | { 30 | var variableObjectExpr = new ObjectVariableExpression("a", typeof(A)); 31 | var listExpr = new ObjectPropertyExpression(variableObjectExpr, typeof(A).GetProperty("Bs")); 32 | var indexExpr = new NumericLiteralExpression(1); 33 | var expr = new ObjectListElementExpression(listExpr, indexExpr); 34 | 35 | B b; 36 | A a = new A 37 | { 38 | Bs = new[] 39 | { 40 | new B { Id = 0 }, 41 | b = new B { Id = 1 }, 42 | } 43 | }; 44 | 45 | object instance = expr.GetInstance(new Dictionary { { "a", a } }); 46 | Assert.Same(b, instance); 47 | } 48 | 49 | [Fact] 50 | public void NumberListElementExpression() 51 | { 52 | var variableObjectExpr = new ObjectVariableExpression("a", typeof(A)); 53 | var listExpr = new ObjectPropertyExpression(variableObjectExpr, typeof(A).GetProperty("Ints")); 54 | var indexExpr = new NumericLiteralExpression(1); 55 | var expr = new NumericListElementExpression(listExpr, indexExpr); 56 | 57 | A a = new A 58 | { 59 | Ints = new List { 1, 2 } 60 | }; 61 | 62 | double value = expr.Evaluate(new Dictionary { { "a", a } }); 63 | Assert.Equal(2.0, value); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator.Tests/ReflectionHelperTest.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dahomey-technologies/Dahomey.ExpressionEvaluator/26aad30283dff5f60a6473b3207857470fd3c839/src/Dahomey.ExpressionEvaluator.Tests/ReflectionHelperTest.cs -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26606.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dahomey.ExpressionEvaluator", "Dahomey.ExpressionEvaluator\Dahomey.ExpressionEvaluator.csproj", "{6B6565E4-4220-473D-BB4A-D5E0CD190A18}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dahomey.ExpressionEvaluator.Tests", "Dahomey.ExpressionEvaluator.Tests\Dahomey.ExpressionEvaluator.Tests.csproj", "{8D8D911E-35D7-427A-ABD4-CD63A0BB9535}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {6B6565E4-4220-473D-BB4A-D5E0CD190A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {6B6565E4-4220-473D-BB4A-D5E0CD190A18}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {6B6565E4-4220-473D-BB4A-D5E0CD190A18}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {6B6565E4-4220-473D-BB4A-D5E0CD190A18}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {8D8D911E-35D7-427A-ABD4-CD63A0BB9535}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {8D8D911E-35D7-427A-ABD4-CD63A0BB9535}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {8D8D911E-35D7-427A-ABD4-CD63A0BB9535}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {8D8D911E-35D7-427A-ABD4-CD63A0BB9535}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Dahomey.ExpressionEvaluator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 7.3 6 | cf. https://github.com/dahomey-technologies/Dahomey.ExpressionEvaluator/releases/tag/$(Version) 7 | Michaël Catanzariti 8 | Dahomey Technologies 9 | Evaluate C# Formulas at Runtime 10 | MIT 11 | https://github.com/dahomey-technologies/Dahomey.ExpressionEvaluator 12 | expression;parser;formula 13 | https://github.com/dahomey-technologies/Dahomey.ExpressionEvaluator.git 14 | Git 15 | https://avatars3.githubusercontent.com/u/29336625?s=200&v=4 16 | Copyright © Dahomey Technologies 2019 17 | false 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/ExpressionLexer.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Globalization; 11 | using System.Text; 12 | 13 | namespace Dahomey.ExpressionEvaluator 14 | { 15 | internal enum TokenType 16 | { 17 | None, 18 | Identifier, 19 | Number, 20 | String, 21 | False, 22 | True, 23 | OpenBracket, 24 | CloseBracket, 25 | OpenParenthesis, 26 | CloseParenthesis, 27 | Dot, 28 | Comma, 29 | Colon, 30 | Plus, 31 | Minus, 32 | Mult, 33 | Div, 34 | Mod, 35 | BitwiseAnd, 36 | BitwiseOr, 37 | BitwiseXor, 38 | BitwiseComplement, 39 | Interrogation, 40 | And, 41 | Or, 42 | Not, 43 | Lt, 44 | Gt, 45 | Eq, 46 | Ne, 47 | Le, 48 | Ge, 49 | LeftShift, 50 | RightShift, 51 | } 52 | 53 | internal class ExpressionLexer 54 | { 55 | private const string NUMBER_CHARS = "0123456789.eE"; 56 | 57 | private readonly string expression; 58 | private int currentPos; 59 | private char currentChar; 60 | private string currentToken; 61 | private TokenType currentTokenType = TokenType.None; 62 | private readonly StringBuilder sb = new StringBuilder(); 63 | 64 | public ExpressionLexer(string expression) 65 | { 66 | this.expression = expression; 67 | Advance(); 68 | NextToken(); 69 | } 70 | 71 | public bool Peek(TokenType tokenType) 72 | { 73 | return currentTokenType == tokenType; 74 | } 75 | 76 | public bool Accept(TokenType tokenType) 77 | { 78 | if (currentTokenType == tokenType) 79 | { 80 | NextToken(); 81 | return true; 82 | } 83 | 84 | return false; 85 | } 86 | 87 | public void Expect(TokenType tokenType) 88 | { 89 | if (!Accept(tokenType)) 90 | { 91 | throw BuildException("expected {0}", tokenType); 92 | } 93 | } 94 | 95 | public double Number() 96 | { 97 | string token = currentToken; 98 | Expect(TokenType.Number); 99 | try 100 | { 101 | return double.Parse(token, CultureInfo.InvariantCulture); 102 | } 103 | catch (Exception ex) 104 | { 105 | throw BuildException(ex, "Cannot parse number from '{0}'", token); 106 | } 107 | } 108 | 109 | public string String() 110 | { 111 | string token = currentToken; 112 | Expect(TokenType.String); 113 | return token; 114 | } 115 | 116 | public string Identifier() 117 | { 118 | string token = currentToken; 119 | Expect(TokenType.Identifier); 120 | return token; 121 | } 122 | 123 | private void NextToken() 124 | { 125 | if (IsEOF()) 126 | { 127 | currentToken = null; 128 | currentTokenType = TokenType.None; 129 | return; 130 | } 131 | 132 | SkipWhiteSpace(); 133 | 134 | sb.Length = 0; 135 | 136 | if (TryScanNumber()) 137 | { 138 | return; 139 | } 140 | 141 | if (TryScanString()) 142 | { 143 | return; 144 | } 145 | 146 | if (TryScanName()) 147 | { 148 | return; 149 | } 150 | 151 | if (TryScanOperatorOrPunctuation()) 152 | { 153 | return; 154 | } 155 | 156 | throw BuildException("Unexpected character {0}", currentChar); 157 | } 158 | 159 | private bool IsEOF() 160 | { 161 | return currentChar == '\0'; 162 | } 163 | 164 | private bool TryScanNumber() 165 | { 166 | if (currentChar < '0' || currentChar > '9') 167 | { 168 | return false; 169 | } 170 | 171 | do 172 | { 173 | if (currentChar == '\\') // escape char 174 | { 175 | Advance(); 176 | 177 | if (IsEOF()) 178 | { 179 | throw BuildException("Invalid escape sequence"); 180 | } 181 | } 182 | 183 | sb.Append(currentChar); 184 | Advance(); 185 | } 186 | while (NUMBER_CHARS.IndexOf(currentChar) != -1); 187 | 188 | currentToken = sb.ToString(); 189 | currentTokenType = TokenType.Number; 190 | return true; 191 | } 192 | 193 | private bool TryScanString() 194 | { 195 | if (currentChar != '"') 196 | { 197 | return false; 198 | } 199 | 200 | Advance(); 201 | 202 | do 203 | { 204 | sb.Append(currentChar); 205 | Advance(); 206 | } 207 | while (!IsEOF() && currentChar != '"'); 208 | 209 | if (currentChar != '"') 210 | { 211 | throw BuildException("Expected '\"'"); 212 | } 213 | Advance(); // skip ending " 214 | 215 | currentToken = sb.ToString(); 216 | currentTokenType = TokenType.String; 217 | return true; 218 | } 219 | 220 | private bool TryScanName() 221 | { 222 | if (!char.IsLetter(currentChar) && currentChar != '_') 223 | { 224 | return false; 225 | } 226 | 227 | do 228 | { 229 | sb.Append(currentChar); 230 | Advance(); 231 | } 232 | while (char.IsLetterOrDigit(currentChar) || currentChar == '_'); 233 | 234 | currentToken = sb.ToString(); 235 | 236 | switch (currentToken) 237 | { 238 | case "true": 239 | currentTokenType = TokenType.True; 240 | break; 241 | 242 | case "false": 243 | currentTokenType = TokenType.False; 244 | break; 245 | 246 | default: 247 | currentTokenType = TokenType.Identifier; 248 | break; 249 | } 250 | 251 | return true; 252 | } 253 | 254 | private bool TryScanOperatorOrPunctuation() 255 | { 256 | switch (currentChar) 257 | { 258 | case '[': 259 | Advance(); 260 | currentTokenType = TokenType.OpenBracket; 261 | break; 262 | 263 | case ']': 264 | Advance(); 265 | currentTokenType = TokenType.CloseBracket; 266 | break; 267 | 268 | case '(': 269 | Advance(); 270 | currentTokenType = TokenType.OpenParenthesis; 271 | break; 272 | 273 | case ')': 274 | Advance(); 275 | currentTokenType = TokenType.CloseParenthesis; 276 | break; 277 | 278 | case '.': 279 | Advance(); 280 | currentTokenType = TokenType.Dot; 281 | break; 282 | 283 | case ',': 284 | Advance(); 285 | currentTokenType = TokenType.Comma; 286 | break; 287 | 288 | case ':': 289 | Advance(); 290 | currentTokenType = TokenType.Colon; 291 | break; 292 | 293 | case '?': 294 | Advance(); 295 | currentTokenType = TokenType.Interrogation; 296 | break; 297 | 298 | case '+': 299 | Advance(); 300 | currentTokenType = TokenType.Plus; 301 | break; 302 | 303 | case '-': 304 | Advance(); 305 | currentTokenType = TokenType.Minus; 306 | break; 307 | 308 | case '*': 309 | Advance(); 310 | currentTokenType = TokenType.Mult; 311 | break; 312 | 313 | case '/': 314 | Advance(); 315 | currentTokenType = TokenType.Div; 316 | break; 317 | 318 | case '%': 319 | Advance(); 320 | currentTokenType = TokenType.Mod; 321 | break; 322 | 323 | case '^': 324 | Advance(); 325 | currentTokenType = TokenType.BitwiseXor; 326 | break; 327 | 328 | case '~': 329 | Advance(); 330 | currentTokenType = TokenType.BitwiseComplement; 331 | break; 332 | 333 | case '&': 334 | Advance(); 335 | if (currentChar == '&') 336 | { 337 | Advance(); 338 | currentTokenType = TokenType.And; 339 | 340 | } 341 | else 342 | { 343 | currentTokenType = TokenType.BitwiseAnd; 344 | } 345 | break; 346 | 347 | case '|': 348 | Advance(); 349 | if (currentChar == '|') 350 | { 351 | Advance(); 352 | currentTokenType = TokenType.Or; 353 | } 354 | else 355 | { 356 | currentTokenType = TokenType.BitwiseOr; 357 | } 358 | break; 359 | 360 | case '<': 361 | Advance(); 362 | if (currentChar == '<') 363 | { 364 | Advance(); 365 | currentTokenType = TokenType.LeftShift; 366 | } 367 | else if (currentChar == '=') 368 | { 369 | Advance(); 370 | currentTokenType = TokenType.Le; 371 | } 372 | else 373 | { 374 | currentTokenType = TokenType.Lt; 375 | } 376 | break; 377 | 378 | case '>': 379 | Advance(); 380 | if (currentChar == '>') 381 | { 382 | Advance(); 383 | currentTokenType = TokenType.RightShift; 384 | } 385 | else if (currentChar == '=') 386 | { 387 | Advance(); 388 | currentTokenType = TokenType.Ge; 389 | } 390 | else 391 | { 392 | currentTokenType = TokenType.Gt; 393 | } 394 | break; 395 | 396 | case '!': 397 | Advance(); 398 | if (currentChar == '=') 399 | { 400 | Advance(); 401 | currentTokenType = TokenType.Ne; 402 | } 403 | else 404 | { 405 | currentTokenType = TokenType.Not; 406 | } 407 | break; 408 | 409 | case '=': 410 | Advance(); 411 | if (currentChar == '=') 412 | { 413 | Advance(); 414 | currentTokenType = TokenType.Eq; 415 | break; 416 | } 417 | throw BuildException("Unexpected character"); 418 | 419 | default: 420 | return false; 421 | } 422 | 423 | currentToken = null; 424 | return true; 425 | } 426 | 427 | private void SkipWhiteSpace() 428 | { 429 | while (!IsEOF()) 430 | { 431 | if (currentChar == ' ' || currentChar == '\t') 432 | { 433 | Advance(); 434 | } 435 | else 436 | { 437 | break; 438 | } 439 | } 440 | } 441 | 442 | private void Advance() 443 | { 444 | if (currentPos >= expression.Length) 445 | { 446 | currentChar = '\0'; 447 | return; 448 | } 449 | 450 | currentChar = expression[currentPos++]; 451 | } 452 | 453 | public Exception BuildException(string message) 454 | { 455 | throw new SyntaxErrorException(string.Format("Syntax error({0}): {1}", currentPos, message)); 456 | } 457 | 458 | public Exception BuildException(string message, params object[] args) 459 | { 460 | return BuildException(string.Format(message, args)); 461 | } 462 | 463 | public Exception BuildException(Exception innerException, string message) 464 | { 465 | throw new SyntaxErrorException(string.Format("Syntax error({0}): {1}", currentPos, message), innerException); 466 | } 467 | 468 | public Exception BuildException(Exception innerException, string message, params object[] args) 469 | { 470 | return BuildException(innerException, string.Format(message, args)); 471 | } 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/ExpressionParser.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using Dahomey.ExpressionEvaluator.Expressions; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Reflection; 13 | 14 | namespace Dahomey.ExpressionEvaluator 15 | { 16 | public class ExpressionParser 17 | { 18 | private ExpressionLexer lexer; 19 | private Dictionary variableTypes = new Dictionary(); 20 | private Dictionary functions = new Dictionary(); 21 | private List assemblies = new List(); 22 | 23 | public void RegisterVariable(string variableName, Type variableType) 24 | { 25 | variableTypes[variableName] = variableType; 26 | } 27 | 28 | public void RegisterVariable(string variableName) 29 | { 30 | variableTypes[variableName] = typeof(T); 31 | } 32 | 33 | public void RegisterFunction(string functionName, Delegate function) 34 | { 35 | functions[functionName] = function; 36 | } 37 | 38 | public void RegisterAssembly(Assembly assembly) 39 | { 40 | assemblies.Add(assembly); 41 | } 42 | 43 | public IBooleanExpression ParseBooleanExpression(string expression) 44 | { 45 | lexer = new ExpressionLexer(expression); 46 | return (IBooleanExpression)Expression(); 47 | } 48 | 49 | public INumericExpression ParseNumericExpression(string expression) 50 | { 51 | lexer = new ExpressionLexer(expression); 52 | return (INumericExpression)Expression(); 53 | } 54 | 55 | private IExpression Expression() 56 | { 57 | return ConditionalExpression(); 58 | } 59 | 60 | private IExpression ConditionalExpression() 61 | { 62 | IExpression expr = LogicalOrExpression(); 63 | 64 | if (lexer.Accept(TokenType.Interrogation)) 65 | { 66 | IExpression leftExpr = Expression(); 67 | lexer.Expect(TokenType.Colon); 68 | IExpression rightExpr = Expression(); 69 | 70 | return new NumericConditionalExpression 71 | { 72 | ConditionExpr = (IBooleanExpression)expr, 73 | LeftExpr = (INumericExpression)leftExpr, 74 | RightExpr = (INumericExpression)rightExpr, 75 | }; 76 | } 77 | 78 | return expr; 79 | } 80 | 81 | private IExpression LogicalOrExpression() 82 | { 83 | IExpression expr = LogicalAndExpression(); 84 | 85 | while (lexer.Accept(TokenType.Or)) 86 | { 87 | expr = new BooleanLogicalExpression 88 | { 89 | Operator = Operator.Or, 90 | LeftExpr = (IBooleanExpression)expr, 91 | RightExpr = (IBooleanExpression)LogicalAndExpression() 92 | }; 93 | } 94 | 95 | return expr; 96 | } 97 | 98 | private IExpression LogicalAndExpression() 99 | { 100 | IExpression expr = BitwiseOrExpression(); 101 | 102 | while (lexer.Accept(TokenType.And)) 103 | { 104 | expr = new BooleanLogicalExpression 105 | { 106 | Operator = Operator.And, 107 | LeftExpr = (IBooleanExpression)expr, 108 | RightExpr = (IBooleanExpression)BitwiseOrExpression() 109 | }; 110 | } 111 | 112 | return expr; 113 | } 114 | 115 | private IExpression BitwiseOrExpression() 116 | { 117 | IExpression expr = BitwiseXorExpression(); 118 | 119 | while (lexer.Accept(TokenType.BitwiseOr)) 120 | { 121 | expr = new NumericArithmeticExpression 122 | { 123 | Operator = Operator.BitwiseOr, 124 | LeftExpr = (INumericExpression)expr, 125 | RightExpr = (INumericExpression)BitwiseXorExpression() 126 | }; 127 | } 128 | 129 | return expr; 130 | } 131 | 132 | private IExpression BitwiseXorExpression() 133 | { 134 | IExpression expr = BitwiseAndExpression(); 135 | 136 | while (lexer.Accept(TokenType.BitwiseXor)) 137 | { 138 | expr = new NumericArithmeticExpression 139 | { 140 | Operator = Operator.BitwiseXor, 141 | LeftExpr = (INumericExpression)expr, 142 | RightExpr = (INumericExpression)BitwiseAndExpression() 143 | }; 144 | } 145 | 146 | return expr; 147 | } 148 | 149 | private IExpression BitwiseAndExpression() 150 | { 151 | IExpression expr = EqualityExpression(); 152 | 153 | while (lexer.Accept(TokenType.BitwiseAnd)) 154 | { 155 | expr = new NumericArithmeticExpression 156 | { 157 | Operator = Operator.BitwiseAnd, 158 | LeftExpr = (INumericExpression)expr, 159 | RightExpr = (INumericExpression)EqualityExpression() 160 | }; 161 | } 162 | 163 | return expr; 164 | } 165 | 166 | private IExpression EqualityExpression() 167 | { 168 | IExpression expr = RelationalExpression(); 169 | 170 | Operator op; 171 | if (lexer.Accept(TokenType.Eq)) 172 | { 173 | op = Operator.Equal; 174 | } 175 | else if (lexer.Accept(TokenType.Ne)) 176 | { 177 | op = Operator.NotEqual; 178 | } 179 | else 180 | { 181 | return expr; 182 | } 183 | 184 | return new NumericComparisonExpression 185 | { 186 | Operator = op, 187 | LeftExpr = (INumericExpression)expr, 188 | RightExpr = (INumericExpression)RelationalExpression() 189 | }; 190 | } 191 | 192 | private IExpression RelationalExpression() 193 | { 194 | IExpression expr = ShiftExpression(); 195 | 196 | Operator op; 197 | if (lexer.Accept(TokenType.Lt)) 198 | { 199 | op = Operator.LessThan; 200 | } 201 | else if (lexer.Accept(TokenType.Le)) 202 | { 203 | op = Operator.LessThanOrEqual; 204 | } 205 | else if (lexer.Accept(TokenType.Ge)) 206 | { 207 | op = Operator.GreaterThanOrEqual; 208 | } 209 | else if (lexer.Accept(TokenType.Gt)) 210 | { 211 | op = Operator.GreaterThan; 212 | } 213 | else 214 | { 215 | return expr; 216 | } 217 | 218 | return new NumericComparisonExpression 219 | { 220 | Operator = op, 221 | LeftExpr = (INumericExpression)expr, 222 | RightExpr = (INumericExpression)ShiftExpression() 223 | }; 224 | } 225 | 226 | private IExpression ShiftExpression() 227 | { 228 | IExpression expr = AdditiveExpression(); 229 | 230 | while (!lexer.Peek(TokenType.None)) 231 | { 232 | Operator op; 233 | if (lexer.Accept(TokenType.LeftShift)) 234 | { 235 | op = Operator.LeftShift; 236 | } 237 | else if (lexer.Accept(TokenType.RightShift)) 238 | { 239 | op = Operator.RightShift; 240 | } 241 | else 242 | { 243 | break; 244 | } 245 | 246 | expr = new NumericArithmeticExpression 247 | { 248 | Operator = op, 249 | LeftExpr = (INumericExpression)expr, 250 | RightExpr = (INumericExpression)AdditiveExpression() 251 | }; 252 | } 253 | 254 | return expr; 255 | } 256 | 257 | private IExpression AdditiveExpression() 258 | { 259 | IExpression expr = MultiplicativeExpression(); 260 | 261 | while (!lexer.Peek(TokenType.None)) 262 | { 263 | Operator op; 264 | if (lexer.Accept(TokenType.Plus)) 265 | { 266 | op = Operator.Plus; 267 | } 268 | else if (lexer.Accept(TokenType.Minus)) 269 | { 270 | op = Operator.Minus; 271 | } 272 | else 273 | { 274 | break; 275 | } 276 | 277 | expr = new NumericArithmeticExpression 278 | { 279 | Operator = op, 280 | LeftExpr = (INumericExpression)expr, 281 | RightExpr = (INumericExpression)MultiplicativeExpression() 282 | }; 283 | } 284 | 285 | return expr; 286 | } 287 | 288 | private IExpression MultiplicativeExpression() 289 | { 290 | IExpression expr = UnaryExpression(); 291 | 292 | while (!lexer.Peek(TokenType.None)) 293 | { 294 | Operator op; 295 | if (lexer.Accept(TokenType.Mult)) 296 | { 297 | op = Operator.Mult; 298 | } 299 | else if (lexer.Accept(TokenType.Div)) 300 | { 301 | op = Operator.Div; 302 | } 303 | else if (lexer.Accept(TokenType.Mod)) 304 | { 305 | op = Operator.Mod; 306 | } 307 | else 308 | { 309 | break; 310 | } 311 | 312 | expr = new NumericArithmeticExpression 313 | { 314 | Operator = op, 315 | LeftExpr = (INumericExpression)expr, 316 | RightExpr = (INumericExpression)UnaryExpression() 317 | }; 318 | } 319 | 320 | return expr; 321 | } 322 | 323 | private IExpression UnaryExpression() 324 | { 325 | if (lexer.Accept(TokenType.Minus)) 326 | { 327 | return new NumericArithmeticExpression 328 | { 329 | Operator = Operator.Minus, 330 | LeftExpr = (INumericExpression)PrimaryExpression() 331 | }; 332 | } 333 | else if (lexer.Accept(TokenType.BitwiseComplement)) 334 | { 335 | return new NumericArithmeticExpression 336 | { 337 | Operator = Operator.BitwiseComplement, 338 | LeftExpr = (INumericExpression)PrimaryExpression() 339 | }; 340 | } 341 | else if (lexer.Accept(TokenType.Not)) 342 | { 343 | return new BooleanLogicalExpression 344 | { 345 | Operator = Operator.Not, 346 | LeftExpr = (IBooleanExpression)PrimaryExpression() 347 | }; 348 | } 349 | else 350 | { 351 | return PrimaryExpression(); 352 | } 353 | } 354 | 355 | private IExpression PrimaryExpression() 356 | { 357 | if (lexer.Peek(TokenType.OpenParenthesis)) 358 | { 359 | return ParenthesizedExpression(); 360 | } 361 | else if (lexer.Peek(TokenType.Identifier)) 362 | { 363 | return VariableExpression(); 364 | } 365 | 366 | return Literal(); 367 | } 368 | 369 | private IExpression VariableExpression() 370 | { 371 | IExpression expr = ElementExpression(VariableOrFunctionExpression()); 372 | 373 | while (lexer.Accept(TokenType.Dot)) 374 | { 375 | IObjectExpression containingObjectExpr = expr as IObjectExpression; 376 | 377 | if (containingObjectExpr == null) 378 | { 379 | throw BuildException("Cannot access property or method on expression {0}", expr); 380 | } 381 | 382 | expr = ElementExpression(MemberExpression(containingObjectExpr)); 383 | } 384 | 385 | return expr; 386 | } 387 | 388 | private IExpression ElementExpression(IExpression expr) 389 | { 390 | if (lexer.Peek(TokenType.OpenBracket)) 391 | { 392 | INumericExpression indexExpr = (INumericExpression)BracketExpression(); 393 | IObjectExpression listExpr = expr as IObjectExpression; 394 | 395 | if (listExpr == null) 396 | { 397 | throw BuildException("Cannot apply indexing with [] on expression", expr); 398 | } 399 | 400 | if (ReflectionHelper.IsNumberList(listExpr.ObjectType)) 401 | { 402 | expr = new NumericListElementExpression(listExpr, indexExpr); 403 | } 404 | else 405 | { 406 | expr = new ObjectListElementExpression(listExpr, indexExpr); 407 | } 408 | } 409 | 410 | return expr; 411 | } 412 | 413 | private IExpression VariableOrFunctionExpression() 414 | { 415 | string identifier = lexer.Identifier(); 416 | 417 | // function 418 | if (lexer.Peek(TokenType.OpenParenthesis)) 419 | { 420 | ListExpression argsExpr = (ListExpression)InvocationExpression(); 421 | 422 | Delegate function; 423 | 424 | if (!functions.TryGetValue(identifier, out function)) 425 | { 426 | throw BuildException("Unknown function '{0}()'", identifier); 427 | } 428 | 429 | MethodInfo methodInfo = function.Method; 430 | 431 | if (ReflectionHelper.IsNumber(methodInfo.ReturnType)) 432 | { 433 | return new NumericFuncExpression(identifier, function, argsExpr); 434 | } 435 | else 436 | { 437 | return new ObjectFuncExpression(identifier, function, argsExpr); 438 | } 439 | } 440 | // variable or enum 441 | else 442 | { 443 | Type identifierType; 444 | // variable 445 | if (variableTypes.TryGetValue(identifier, out identifierType)) 446 | { 447 | if (ReflectionHelper.IsNumber(identifierType)) 448 | { 449 | return new NumericVariableExpression(identifier, identifierType); 450 | } 451 | else 452 | { 453 | return new ObjectVariableExpression(identifier, identifierType); 454 | } 455 | } 456 | else 457 | { 458 | identifierType = ReflectionHelper.GetType(assemblies, identifier); 459 | // enum 460 | 461 | if (identifier != null && identifierType.IsEnum) 462 | { 463 | lexer.Expect(TokenType.Dot); 464 | string enumValue = lexer.Identifier(); 465 | 466 | Enum value = (Enum)Enum.Parse(identifierType, enumValue); 467 | return new EnumLiteralExpression(value); 468 | } 469 | else 470 | { 471 | throw BuildException(string.Format("Unknown variable '{0}'", identifier)); 472 | } 473 | } 474 | } 475 | } 476 | 477 | private IExpression MemberExpression(IObjectExpression containingObjectExpr) 478 | { 479 | string identifier = lexer.Identifier(); 480 | 481 | // method 482 | if (lexer.Peek(TokenType.OpenParenthesis)) 483 | { 484 | ListExpression argsExpr = (ListExpression)InvocationExpression(); 485 | 486 | MethodInfo methodInfo = containingObjectExpr.ObjectType.GetMethod(identifier); 487 | Type returnType = methodInfo.ReturnType; 488 | 489 | if (ReflectionHelper.IsNumber(returnType)) 490 | { 491 | return new NumericMethodExpression(containingObjectExpr, methodInfo, argsExpr); 492 | } 493 | else 494 | { 495 | return new ObjectMethodExpression(containingObjectExpr, methodInfo, argsExpr); 496 | } 497 | } 498 | // property 499 | else 500 | { 501 | PropertyInfo propertyInfo = containingObjectExpr.ObjectType.GetProperty(identifier); 502 | Type propertyType = propertyInfo.PropertyType; 503 | 504 | if (ReflectionHelper.IsNumber(propertyType)) 505 | { 506 | return new NumericPropertyExpression(containingObjectExpr, propertyInfo); 507 | } 508 | else 509 | { 510 | return new ObjectPropertyExpression(containingObjectExpr, propertyInfo); 511 | } 512 | } 513 | } 514 | 515 | private IExpression Literal() 516 | { 517 | if (lexer.Accept(TokenType.True)) 518 | { 519 | return new BooleanLiteralExpression(true); 520 | } 521 | 522 | if (lexer.Accept(TokenType.False)) 523 | { 524 | return new BooleanLiteralExpression(false); 525 | } 526 | 527 | if (lexer.Peek(TokenType.Number)) 528 | { 529 | return new NumericLiteralExpression(lexer.Number()); 530 | } 531 | 532 | if (lexer.Peek(TokenType.String)) 533 | { 534 | return new StringLiteralExpression(lexer.String()); 535 | } 536 | 537 | throw BuildException("Expected boolean, number or string literal"); 538 | } 539 | 540 | private IExpression ParenthesizedExpression() 541 | { 542 | lexer.Expect(TokenType.OpenParenthesis); 543 | IExpression expr = Expression(); 544 | lexer.Expect(TokenType.CloseParenthesis); 545 | return expr; 546 | } 547 | 548 | private IExpression BracketExpression() 549 | { 550 | lexer.Expect(TokenType.OpenBracket); 551 | IExpression expr = Expression(); 552 | lexer.Expect(TokenType.CloseBracket); 553 | return expr; 554 | } 555 | 556 | private IExpression InvocationExpression() 557 | { 558 | lexer.Expect(TokenType.OpenParenthesis); 559 | 560 | List args = new List(); 561 | ListExpression argsExpr = new ListExpression(args); 562 | 563 | if (lexer.Accept(TokenType.CloseParenthesis)) 564 | { 565 | return argsExpr; 566 | } 567 | 568 | do 569 | { 570 | args.Add(Expression()); 571 | } 572 | while (lexer.Accept(TokenType.Comma)); 573 | 574 | lexer.Expect(TokenType.CloseParenthesis); 575 | 576 | return argsExpr; 577 | } 578 | 579 | public Exception BuildException(string message) 580 | { 581 | return lexer.BuildException(message); 582 | } 583 | 584 | public Exception BuildException(string message, params object[] args) 585 | { 586 | return lexer.BuildException(message, args); 587 | } 588 | } 589 | } 590 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/BooleanLiteralExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System.Collections.Generic; 10 | 11 | namespace Dahomey.ExpressionEvaluator 12 | { 13 | public class BooleanLiteralExpression : IBooleanExpression 14 | { 15 | private bool value; 16 | 17 | public BooleanLiteralExpression(bool value) 18 | { 19 | this.value = value; 20 | } 21 | 22 | public bool Evaluate(Dictionary variables) 23 | { 24 | return value; 25 | } 26 | 27 | public override string ToString() 28 | { 29 | return value ? "true" : "false"; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/BooleanLogicalExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace Dahomey.ExpressionEvaluator 14 | { 15 | public class BooleanLogicalExpression : IBooleanExpression 16 | { 17 | public Operator Operator { get; set; } 18 | public IBooleanExpression LeftExpr { get; set; } 19 | public IBooleanExpression RightExpr { get; set; } 20 | 21 | public bool Evaluate(Dictionary variables) 22 | { 23 | switch (Operator) 24 | { 25 | case Operator.And: 26 | return LeftExpr.Evaluate(variables) && RightExpr.Evaluate(variables); 27 | 28 | case Operator.Or: 29 | return LeftExpr.Evaluate(variables) || RightExpr.Evaluate(variables); 30 | 31 | case Operator.Not: 32 | return !LeftExpr.Evaluate(variables); 33 | 34 | default: 35 | throw new NotSupportedException(string.Format("Operator {0} not supported", Operator)); 36 | } 37 | } 38 | 39 | public override string ToString() 40 | { 41 | StringBuilder sb = new StringBuilder(); 42 | 43 | if (LeftExpr != null) 44 | { 45 | sb.Append(LeftExpr).Append(' '); 46 | } 47 | 48 | if (RightExpr != null) 49 | { 50 | sb.Append(RightExpr).Append(' '); 51 | } 52 | 53 | sb.Append(Operator.PrettyPrint()); 54 | 55 | return sb.ToString(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/EnumLiteralExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | 12 | namespace Dahomey.ExpressionEvaluator 13 | { 14 | public class EnumLiteralExpression : INumericExpression 15 | { 16 | private Enum enumValue; 17 | private double value; 18 | 19 | public EnumLiteralExpression(Enum value) 20 | { 21 | this.value = Convert.ToDouble(value); 22 | this.enumValue = value; 23 | } 24 | 25 | public double Evaluate(Dictionary variables) 26 | { 27 | return value; 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return string.Format("{0}.{1}", enumValue.GetType().Name, enumValue.ToString()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/IBooleanExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System.Collections.Generic; 10 | 11 | namespace Dahomey.ExpressionEvaluator 12 | { 13 | public interface IBooleanExpression : IExpression 14 | { 15 | bool Evaluate(Dictionary variables = null); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/IExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | namespace Dahomey.ExpressionEvaluator 10 | { 11 | public interface IExpression 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/INumericExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System.Collections.Generic; 10 | 11 | namespace Dahomey.ExpressionEvaluator 12 | { 13 | public interface INumericExpression : IExpression 14 | { 15 | double Evaluate(Dictionary variables = null); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/IObjectExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | 12 | namespace Dahomey.ExpressionEvaluator 13 | { 14 | public interface IObjectExpression : IExpression 15 | { 16 | Type ObjectType { get; } 17 | object GetInstance(Dictionary variables); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/IStringExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System.Collections.Generic; 10 | 11 | namespace Dahomey.ExpressionEvaluator 12 | { 13 | public interface IStringExpression : IExpression 14 | { 15 | string Evaluate(Dictionary variables); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/ListExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | #if NET35 11 | using System.Linq; 12 | #endif 13 | using System.Collections.Generic; 14 | using System.Reflection; 15 | 16 | namespace Dahomey.ExpressionEvaluator.Expressions 17 | { 18 | public class ListExpression : IExpression 19 | { 20 | public List Expressions { get; private set; } 21 | 22 | public ListExpression(List expressions) 23 | { 24 | Expressions = expressions; 25 | } 26 | 27 | public Func, T> GetItemGetter(int index) 28 | { 29 | IExpression itemExpr = Expressions[index]; 30 | 31 | Type itemType = typeof(T); 32 | 33 | if (ReflectionHelper.IsNumber(itemType)) 34 | { 35 | INumericExpression numericExpr = (INumericExpression)itemExpr; 36 | 37 | Func converter = ReflectionHelper.GenerateFromDoubleConverter(); 38 | return variables => converter(numericExpr.Evaluate(variables)); 39 | } 40 | else if (itemType == typeof(bool)) 41 | { 42 | MethodInfo generateGetterMethod = GetType() 43 | .GetMethod("GetBooleanItemGetter", BindingFlags.NonPublic | BindingFlags.Static); 44 | 45 | Func, T>> generateGetterDelegate = 46 | ReflectionHelper.CreateDelegate, T>>(generateGetterMethod); 47 | 48 | IBooleanExpression booleanExpr = (IBooleanExpression)itemExpr; 49 | return generateGetterDelegate(booleanExpr); 50 | } 51 | else if (itemType == typeof(string)) 52 | { 53 | MethodInfo generateGetterMethod = GetType() 54 | .GetMethod("GetStringItemGetter", BindingFlags.NonPublic | BindingFlags.Static); 55 | 56 | Func, T>> generateGetterDelegate = 57 | ReflectionHelper.CreateDelegate, T>>(generateGetterMethod); 58 | 59 | IStringExpression booleanExpr = (IStringExpression)itemExpr; 60 | return generateGetterDelegate(booleanExpr); 61 | } 62 | else 63 | { 64 | IObjectExpression objectExpr = (IObjectExpression)itemExpr; 65 | 66 | return variables => (T)objectExpr.GetInstance(variables); 67 | } 68 | } 69 | 70 | private static Func, bool> GetBooleanItemGetter(IBooleanExpression booleanExpr) 71 | { 72 | return variables => booleanExpr.Evaluate(variables); 73 | } 74 | 75 | private static Func, string> GetStringItemGetter(IStringExpression stringExpr) 76 | { 77 | return variables => stringExpr.Evaluate(variables); 78 | } 79 | 80 | public override string ToString() 81 | { 82 | #if NET35 83 | return string.Join(",", Expressions.Select(e => e.ToString()).ToArray()); 84 | #else 85 | return string.Join(",", Expressions); 86 | #endif 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericArithmeticExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace Dahomey.ExpressionEvaluator 14 | { 15 | public class NumericArithmeticExpression : INumericExpression 16 | { 17 | public Operator Operator { get; set; } 18 | public INumericExpression LeftExpr { get; set; } 19 | public INumericExpression RightExpr { get; set; } 20 | 21 | public double Evaluate(Dictionary variables) 22 | { 23 | switch (Operator) 24 | { 25 | case Operator.Plus: 26 | return RightExpr == null ? 27 | LeftExpr.Evaluate(variables) : 28 | LeftExpr.Evaluate(variables) + RightExpr.Evaluate(variables); 29 | 30 | case Operator.Minus: 31 | return RightExpr == null ? 32 | -LeftExpr.Evaluate(variables) : 33 | LeftExpr.Evaluate(variables) - RightExpr.Evaluate(variables); 34 | 35 | case Operator.Mult: 36 | return LeftExpr.Evaluate(variables) * RightExpr.Evaluate(variables); 37 | 38 | case Operator.Div: 39 | return LeftExpr.Evaluate(variables) / RightExpr.Evaluate(variables); 40 | 41 | case Operator.Mod: 42 | return LeftExpr.Evaluate(variables) % RightExpr.Evaluate(variables); 43 | 44 | case Operator.BitwiseAnd: 45 | return (int)LeftExpr.Evaluate(variables) & (int)RightExpr.Evaluate(variables); 46 | 47 | case Operator.BitwiseOr: 48 | return (int)LeftExpr.Evaluate(variables) | (int)RightExpr.Evaluate(variables); 49 | 50 | case Operator.BitwiseXor: 51 | return (int)LeftExpr.Evaluate(variables) ^ (int)RightExpr.Evaluate(variables); 52 | 53 | case Operator.BitwiseComplement: 54 | return ~(int)LeftExpr.Evaluate(variables); 55 | 56 | case Operator.LeftShift: 57 | return (int)LeftExpr.Evaluate(variables) << (int)RightExpr.Evaluate(variables); 58 | 59 | case Operator.RightShift: 60 | return (int)LeftExpr.Evaluate(variables) >> (int)RightExpr.Evaluate(variables); 61 | 62 | default: 63 | throw new NotSupportedException(string.Format("operator {0} not support", Operator)); 64 | } 65 | } 66 | 67 | public override string ToString() 68 | { 69 | StringBuilder sb = new StringBuilder(); 70 | 71 | if (LeftExpr != null) 72 | { 73 | sb.Append(LeftExpr).Append(' '); 74 | } 75 | 76 | if (RightExpr != null) 77 | { 78 | sb.Append(RightExpr).Append(' '); 79 | } 80 | 81 | sb.Append(Operator.PrettyPrint()); 82 | 83 | return sb.ToString(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericComparisonExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace Dahomey.ExpressionEvaluator 14 | { 15 | public class NumericComparisonExpression : IBooleanExpression 16 | { 17 | public Operator Operator { get; set; } 18 | public INumericExpression LeftExpr { get; set; } 19 | public INumericExpression RightExpr { get; set; } 20 | 21 | public bool Evaluate(Dictionary variables) 22 | { 23 | switch (Operator) 24 | { 25 | case Operator.LessThan: 26 | return LeftExpr.Evaluate(variables) < RightExpr.Evaluate(variables); 27 | 28 | case Operator.LessThanOrEqual: 29 | return LeftExpr.Evaluate(variables) <= RightExpr.Evaluate(variables); 30 | 31 | case Operator.Equal: 32 | return Math.Abs(LeftExpr.Evaluate(variables) - RightExpr.Evaluate(variables)) < double.Epsilon; 33 | 34 | case Operator.NotEqual: 35 | return Math.Abs(LeftExpr.Evaluate(variables) - RightExpr.Evaluate(variables)) > double.Epsilon; 36 | 37 | case Operator.GreaterThanOrEqual: 38 | return LeftExpr.Evaluate(variables) >= RightExpr.Evaluate(variables); 39 | 40 | case Operator.GreaterThan: 41 | return LeftExpr.Evaluate(variables) > RightExpr.Evaluate(variables); 42 | 43 | default: 44 | throw new NotSupportedException(String.Format("Operator {0} not supported", Operator)); 45 | } 46 | } 47 | 48 | public override string ToString() 49 | { 50 | StringBuilder sb = new StringBuilder(); 51 | 52 | if (LeftExpr != null) 53 | { 54 | sb.Append(LeftExpr).Append(' '); 55 | } 56 | 57 | if (RightExpr != null) 58 | { 59 | sb.Append(RightExpr).Append(' '); 60 | } 61 | 62 | sb.Append(Operator.PrettyPrint()); 63 | 64 | return sb.ToString(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericConditionalExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System.Collections.Generic; 10 | using System.Text; 11 | 12 | namespace Dahomey.ExpressionEvaluator 13 | { 14 | public class NumericConditionalExpression : INumericExpression 15 | { 16 | public IBooleanExpression ConditionExpr { get; set; } 17 | public INumericExpression LeftExpr { get; set; } 18 | public INumericExpression RightExpr { get; set; } 19 | 20 | public double Evaluate(Dictionary variables) 21 | { 22 | return ConditionExpr.Evaluate(variables) ? LeftExpr.Evaluate(variables) : RightExpr.Evaluate(variables); 23 | } 24 | 25 | public override string ToString() 26 | { 27 | StringBuilder sb = new StringBuilder(); 28 | 29 | if (ConditionExpr != null) 30 | { 31 | sb.Append(LeftExpr).Append(' '); 32 | } 33 | 34 | if (LeftExpr != null) 35 | { 36 | sb.Append(LeftExpr).Append(' '); 37 | } 38 | 39 | if (RightExpr != null) 40 | { 41 | sb.Append(RightExpr).Append(' '); 42 | } 43 | 44 | sb.Append("?:"); 45 | 46 | return sb.ToString(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericFuncExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using Dahomey.ExpressionEvaluator.Expressions; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Reflection; 13 | 14 | namespace Dahomey.ExpressionEvaluator 15 | { 16 | public class NumericFuncExpression : INumericExpression 17 | { 18 | private string functionName; 19 | private ListExpression argumentsExpr; 20 | private Func, double> evaluator; 21 | 22 | public NumericFuncExpression(string functionName, Delegate function, ListExpression argumentsExpr) 23 | { 24 | this.functionName = functionName; 25 | this.argumentsExpr = argumentsExpr; 26 | 27 | MethodInfo methodInfo = function.Method; 28 | Type returnType = methodInfo.ReturnType; 29 | MethodInfo generateEvaluatorMethod; 30 | ParameterInfo[] parameters = methodInfo.GetParameters(); 31 | 32 | switch (parameters.Length) 33 | { 34 | case 0: 35 | generateEvaluatorMethod = GetType() 36 | .GetMethod("GenerateFunctor0", BindingFlags.NonPublic | BindingFlags.Static) 37 | .MakeGenericMethod(returnType); 38 | break; 39 | 40 | case 1: 41 | generateEvaluatorMethod = GetType() 42 | .GetMethod("GenerateFunctor1", BindingFlags.NonPublic | BindingFlags.Static) 43 | .MakeGenericMethod(parameters[0].ParameterType, returnType); 44 | break; 45 | 46 | case 2: 47 | generateEvaluatorMethod = GetType() 48 | .GetMethod("GenerateFunctor2", BindingFlags.NonPublic | BindingFlags.Static) 49 | .MakeGenericMethod( 50 | parameters[0].ParameterType, 51 | parameters[1].ParameterType, 52 | returnType); 53 | break; 54 | 55 | case 3: 56 | generateEvaluatorMethod = GetType() 57 | .GetMethod("GenerateFunctor3", BindingFlags.NonPublic | BindingFlags.Static) 58 | .MakeGenericMethod( 59 | parameters[0].ParameterType, 60 | parameters[1].ParameterType, 61 | parameters[2].ParameterType, 62 | returnType); 63 | break; 64 | 65 | default: 66 | throw new NotSupportedException(); 67 | } 68 | 69 | var generateEvaluatorDelegate = ReflectionHelper 70 | .CreateDelegate, double>>(generateEvaluatorMethod); 71 | 72 | evaluator = generateEvaluatorDelegate(function, argumentsExpr); 73 | } 74 | 75 | public double Evaluate(Dictionary variables) 76 | { 77 | return evaluator(variables); 78 | } 79 | 80 | private static Func, double> GenerateFunctor0(Delegate function, ListExpression argumentsExpr) 81 | { 82 | Func invoker = (Func)function; 83 | Func converter = ReflectionHelper.GenerateConverter(); 84 | 85 | return variables => converter(invoker()); 86 | } 87 | 88 | private static Func, double> GenerateFunctor1(Delegate function, ListExpression argumentsExpr) 89 | { 90 | Func invoker = (Func)function; 91 | Func converter = ReflectionHelper.GenerateConverter(); 92 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 93 | 94 | return variables => converter(invoker(argGetter1(variables))); 95 | } 96 | 97 | private static Func, double> GenerateFunctor2(Delegate function, ListExpression argumentsExpr) 98 | { 99 | Func invoker = (Func)function; 100 | Func converter = ReflectionHelper.GenerateConverter(); 101 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 102 | Func, TP2> argGetter2 = argumentsExpr.GetItemGetter(1); 103 | 104 | return variables => converter(invoker(argGetter1(variables), argGetter2(variables))); 105 | } 106 | 107 | private static Func, double> GenerateFunctor3(Delegate function, ListExpression argumentsExpr) 108 | { 109 | Func invoker = (Func)function; 110 | Func converter = ReflectionHelper.GenerateConverter(); 111 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 112 | Func, TP2> argGetter2 = argumentsExpr.GetItemGetter(1); 113 | Func, TP3> argGetter3 = argumentsExpr.GetItemGetter(2); 114 | 115 | return variables => converter(invoker(argGetter1(variables), argGetter2(variables), argGetter3(variables))); 116 | } 117 | 118 | public override string ToString() 119 | { 120 | return string.Format("{0}({1})", functionName, argumentsExpr); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericListElementExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Reflection; 12 | 13 | namespace Dahomey.ExpressionEvaluator 14 | { 15 | public class NumericListElementExpression : INumericExpression 16 | { 17 | private readonly Func evaluator; 18 | private IObjectExpression listExpr; 19 | public INumericExpression indexExpr; 20 | 21 | public NumericListElementExpression(IObjectExpression listExpr, INumericExpression indexExpr) 22 | { 23 | this.listExpr = listExpr; 24 | this.indexExpr = indexExpr; 25 | 26 | Type objectType = listExpr.ObjectType; 27 | Type itemType; 28 | 29 | if (objectType.IsArray) 30 | { 31 | itemType = objectType.GetElementType(); 32 | } 33 | else if (ReflectionHelper.IsList(objectType)) 34 | { 35 | itemType = objectType.GetGenericArguments()[0]; 36 | } 37 | else 38 | { 39 | throw new NotSupportedException(); 40 | } 41 | 42 | MethodInfo generateEvaluatorMethod = GetType() 43 | .GetMethod("GenerateEvaluator", BindingFlags.NonPublic | BindingFlags.Static) 44 | .MakeGenericMethod(itemType); 45 | 46 | Func> generateEvaluatorDelegate = 47 | ReflectionHelper.CreateDelegate>(generateEvaluatorMethod); 48 | 49 | evaluator = generateEvaluatorDelegate(); 50 | } 51 | 52 | public double Evaluate(Dictionary variables) 53 | { 54 | object listInstance = listExpr.GetInstance(variables); 55 | return evaluator(listInstance, (int)indexExpr.Evaluate(variables)); 56 | } 57 | 58 | private static Func GenerateEvaluator() 59 | { 60 | Func converter = ReflectionHelper.GenerateConverter(); 61 | return (obj, idx) => converter(((IList)obj)[idx]); 62 | } 63 | 64 | public override string ToString() 65 | { 66 | return string.Format("{0}[{1}]", listExpr, indexExpr); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericLiteralExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System.Collections.Generic; 10 | using System.Globalization; 11 | 12 | namespace Dahomey.ExpressionEvaluator 13 | { 14 | public class NumericLiteralExpression : INumericExpression 15 | { 16 | private double value; 17 | 18 | public NumericLiteralExpression(double value) 19 | { 20 | this.value = value; 21 | } 22 | 23 | public double Evaluate(Dictionary variables) 24 | { 25 | return value; 26 | } 27 | 28 | public override string ToString() 29 | { 30 | return value.ToString(CultureInfo.InvariantCulture); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericMethodExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using Dahomey.ExpressionEvaluator.Expressions; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Reflection; 13 | using System.Text; 14 | 15 | namespace Dahomey.ExpressionEvaluator 16 | { 17 | public class NumericMethodExpression : INumericExpression 18 | { 19 | private IObjectExpression containingObject; 20 | private MethodInfo methodInfo; 21 | private ListExpression argumentsExpr; 22 | private Func, object, double> evaluator; 23 | 24 | public NumericMethodExpression(IObjectExpression containingObject, MethodInfo methodInfo, ListExpression argumentsExpr) 25 | { 26 | this.containingObject = containingObject; 27 | this.methodInfo = methodInfo; 28 | this.argumentsExpr = argumentsExpr; 29 | 30 | Type containingObjectType = containingObject.ObjectType; 31 | Type returnType = methodInfo.ReturnType; 32 | MethodInfo generateEvaluatorMethod; 33 | ParameterInfo[] parameters = methodInfo.GetParameters(); 34 | 35 | switch (parameters.Length) 36 | { 37 | case 0: 38 | generateEvaluatorMethod = GetType() 39 | .GetMethod("GenerateFunctor0", BindingFlags.NonPublic | BindingFlags.Static) 40 | .MakeGenericMethod(containingObjectType, returnType); 41 | break; 42 | 43 | case 1: 44 | generateEvaluatorMethod = GetType() 45 | .GetMethod("GenerateFunctor1", BindingFlags.NonPublic | BindingFlags.Static) 46 | .MakeGenericMethod(containingObjectType, parameters[0].ParameterType, returnType); 47 | break; 48 | 49 | case 2: 50 | generateEvaluatorMethod = GetType() 51 | .GetMethod("GenerateFunctor2", BindingFlags.NonPublic | BindingFlags.Static) 52 | .MakeGenericMethod(containingObjectType, 53 | parameters[0].ParameterType, 54 | parameters[1].ParameterType, 55 | returnType); 56 | break; 57 | 58 | case 3: 59 | generateEvaluatorMethod = GetType() 60 | .GetMethod("GenerateFunctor3", BindingFlags.NonPublic | BindingFlags.Static) 61 | .MakeGenericMethod(containingObjectType, 62 | parameters[0].ParameterType, 63 | parameters[1].ParameterType, 64 | parameters[2].ParameterType, 65 | returnType); 66 | break; 67 | 68 | default: 69 | throw new NotSupportedException(); 70 | } 71 | 72 | var generateEvaluatorDelegate = ReflectionHelper 73 | .CreateDelegate, object, double>>(generateEvaluatorMethod); 74 | 75 | evaluator = generateEvaluatorDelegate(methodInfo, argumentsExpr); 76 | } 77 | 78 | public double Evaluate(Dictionary variables) 79 | { 80 | object instance = containingObject.GetInstance(variables); 81 | return evaluator(variables, instance); 82 | } 83 | 84 | private static Func, object, double> GenerateFunctor0(MethodInfo methodInfo, ListExpression argumentsExpr) 85 | { 86 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 87 | Func converter = ReflectionHelper.GenerateConverter(); 88 | 89 | return (variables, obj) => converter(invoker((T)obj)); 90 | } 91 | 92 | private static Func, object, double> GenerateFunctor1(MethodInfo methodInfo, ListExpression argumentsExpr) 93 | { 94 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 95 | Func converter = ReflectionHelper.GenerateConverter(); 96 | Func, TP> argGetter = argumentsExpr.GetItemGetter(0); 97 | 98 | return (variables, obj) => converter(invoker((T)obj, argGetter(variables))); 99 | } 100 | 101 | private static Func, object, double> GenerateFunctor2(MethodInfo methodInfo, ListExpression argumentsExpr) 102 | { 103 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 104 | Func converter = ReflectionHelper.GenerateConverter(); 105 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 106 | Func, TP2> argGetter2 = argumentsExpr.GetItemGetter(1); 107 | 108 | return (variables, obj) => converter(invoker((T)obj, argGetter1(variables), argGetter2(variables))); 109 | } 110 | 111 | private static Func, object, double> GenerateFunctor3(MethodInfo methodInfo, ListExpression argumentsExpr) 112 | { 113 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 114 | Func converter = ReflectionHelper.GenerateConverter(); 115 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 116 | Func, TP2> argGetter2 = argumentsExpr.GetItemGetter(1); 117 | Func, TP3> argGetter3 = argumentsExpr.GetItemGetter(2); 118 | 119 | return (variables, obj) => converter(invoker((T)obj, argGetter1(variables), argGetter2(variables), argGetter3(variables))); 120 | } 121 | 122 | public override string ToString() 123 | { 124 | return string.Format("{0}.{1}({2})", containingObject, methodInfo.Name, argumentsExpr); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericPropertyExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Reflection; 12 | using System.Text; 13 | 14 | namespace Dahomey.ExpressionEvaluator 15 | { 16 | public class NumericPropertyExpression : INumericExpression 17 | { 18 | private IObjectExpression containingObject; 19 | private PropertyInfo propertyInfo; 20 | private Func evaluator; 21 | 22 | public NumericPropertyExpression(IObjectExpression containingObject, PropertyInfo propertyInfo) 23 | { 24 | this.containingObject = containingObject; 25 | this.propertyInfo = propertyInfo; 26 | 27 | Type containingObjectType = containingObject.ObjectType; 28 | Type propertyType = propertyInfo.PropertyType; 29 | 30 | MethodInfo generateEvaluatorMethod = GetType() 31 | .GetMethod("GenerateEvaluator", BindingFlags.NonPublic | BindingFlags.Static) 32 | .MakeGenericMethod(containingObjectType, propertyType); 33 | 34 | Func> generateEvaluatorDelegate = 35 | ReflectionHelper.CreateDelegate>(generateEvaluatorMethod); 36 | 37 | evaluator = generateEvaluatorDelegate(propertyInfo); 38 | } 39 | 40 | public double Evaluate(Dictionary variables) 41 | { 42 | object instance = containingObject.GetInstance(variables); 43 | return evaluator(instance); 44 | } 45 | 46 | private static Func GenerateEvaluator(PropertyInfo propertyInfo) 47 | { 48 | Func getter = ReflectionHelper.CreateDelegate(propertyInfo); 49 | Func converter = ReflectionHelper.GenerateConverter(); 50 | 51 | return obj => converter(getter((T)obj)); 52 | } 53 | 54 | public override string ToString() 55 | { 56 | StringBuilder sb = new StringBuilder(); 57 | sb.Append(containingObject).Append('.').Append(propertyInfo.Name); 58 | return sb.ToString(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/NumericVariableExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | 12 | namespace Dahomey.ExpressionEvaluator 13 | { 14 | public class NumericVariableExpression : INumericExpression 15 | { 16 | private string variableName; 17 | private Type variableType; 18 | 19 | public NumericVariableExpression(string variableName, Type variableType) 20 | { 21 | this.variableName = variableName; 22 | this.variableType = variableType; 23 | } 24 | 25 | public double Evaluate(Dictionary variables) 26 | { 27 | return Convert.ToDouble(variables[variableName]); 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return variableName; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/ObjectFuncExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using Dahomey.ExpressionEvaluator.Expressions; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Reflection; 13 | 14 | namespace Dahomey.ExpressionEvaluator 15 | { 16 | public class ObjectFuncExpression : IObjectExpression 17 | { 18 | private string functionName; 19 | private ListExpression argumentsExpr; 20 | private Func, object> evaluator; 21 | public Type ObjectType { get; private set; } 22 | 23 | public ObjectFuncExpression(string functionName, Delegate function, ListExpression argumentsExpr) 24 | { 25 | this.functionName = functionName; 26 | this.functionName = functionName; 27 | this.argumentsExpr = argumentsExpr; 28 | 29 | MethodInfo methodInfo = function.Method; 30 | ObjectType = methodInfo.ReturnType; 31 | Type returnType = methodInfo.ReturnType; 32 | MethodInfo generateEvaluatorMethod; 33 | ParameterInfo[] parameters = methodInfo.GetParameters(); 34 | 35 | switch (parameters.Length) 36 | { 37 | case 0: 38 | generateEvaluatorMethod = GetType() 39 | .GetMethod("GenerateFunctor0", BindingFlags.NonPublic | BindingFlags.Static) 40 | .MakeGenericMethod(returnType); 41 | break; 42 | 43 | case 1: 44 | generateEvaluatorMethod = GetType() 45 | .GetMethod("GenerateFunctor1", BindingFlags.NonPublic | BindingFlags.Static) 46 | .MakeGenericMethod(parameters[0].ParameterType, returnType); 47 | break; 48 | 49 | case 2: 50 | generateEvaluatorMethod = GetType() 51 | .GetMethod("GenerateFunctor2", BindingFlags.NonPublic | BindingFlags.Static) 52 | .MakeGenericMethod( 53 | parameters[0].ParameterType, 54 | parameters[1].ParameterType, 55 | returnType); 56 | break; 57 | 58 | case 3: 59 | generateEvaluatorMethod = GetType() 60 | .GetMethod("GenerateFunctor3", BindingFlags.NonPublic | BindingFlags.Static) 61 | .MakeGenericMethod( 62 | parameters[0].ParameterType, 63 | parameters[1].ParameterType, 64 | parameters[2].ParameterType, 65 | returnType); 66 | break; 67 | 68 | default: 69 | throw new NotSupportedException(); 70 | } 71 | 72 | var generateEvaluatorDelegate = ReflectionHelper 73 | .CreateDelegate, object>>(generateEvaluatorMethod); 74 | 75 | evaluator = generateEvaluatorDelegate(function, argumentsExpr); 76 | } 77 | 78 | public object GetInstance(Dictionary variables) 79 | { 80 | return evaluator(variables); 81 | } 82 | 83 | private static Func, object> GenerateFunctor0(MethodInfo methodInfo, ListExpression argumentsExpr) 84 | { 85 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 86 | 87 | return variables => invoker(); 88 | } 89 | 90 | private static Func, object> GenerateFunctor1(Delegate function, ListExpression argumentsExpr) 91 | { 92 | Func invoker = (Func)function; 93 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 94 | 95 | return variables => invoker(argGetter1(variables)); 96 | } 97 | 98 | private static Func, object> GenerateFunctor2(MethodInfo methodInfo, ListExpression argumentsExpr) 99 | { 100 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 101 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 102 | Func, TP2> argGetter2 = argumentsExpr.GetItemGetter(1); 103 | 104 | return variables => invoker(argGetter1(variables), argGetter2(variables)); 105 | } 106 | 107 | private static Func, object> GenerateFunctor3(MethodInfo methodInfo, ListExpression argumentsExpr) 108 | { 109 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 110 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 111 | Func, TP2> argGetter2 = argumentsExpr.GetItemGetter(1); 112 | Func, TP3> argGetter3 = argumentsExpr.GetItemGetter(2); 113 | 114 | return variables => invoker(argGetter1(variables), argGetter2(variables), argGetter3(variables)); 115 | } 116 | 117 | public override string ToString() 118 | { 119 | return string.Format("{0}({1})", functionName, argumentsExpr); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/ObjectListElementExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Reflection; 12 | 13 | namespace Dahomey.ExpressionEvaluator 14 | { 15 | public class ObjectListElementExpression : IObjectExpression 16 | { 17 | private readonly Func evaluator; 18 | private IObjectExpression listExpr; 19 | public INumericExpression indexExpr; 20 | public Type ObjectType { get; private set; } 21 | 22 | public ObjectListElementExpression(IObjectExpression listExpr, INumericExpression indexExpr) 23 | { 24 | this.listExpr = listExpr; 25 | this.indexExpr = indexExpr; 26 | 27 | Type objectType = listExpr.ObjectType; 28 | Type itemType; 29 | 30 | if (objectType.IsArray) 31 | { 32 | itemType = objectType.GetElementType(); 33 | } 34 | else if (ReflectionHelper.IsList(objectType)) 35 | { 36 | itemType = objectType.GetGenericArguments()[0]; 37 | } 38 | else 39 | { 40 | throw new NotSupportedException(); 41 | } 42 | 43 | MethodInfo generateEvaluatorMethod = GetType() 44 | .GetMethod("GenerateEvaluator", BindingFlags.NonPublic | BindingFlags.Static) 45 | .MakeGenericMethod(itemType); 46 | 47 | Func> generateEvaluatorDelegate = 48 | ReflectionHelper.CreateDelegate>(generateEvaluatorMethod); 49 | 50 | evaluator = generateEvaluatorDelegate(); 51 | } 52 | 53 | public object GetInstance(Dictionary variables) 54 | { 55 | object listInstance = listExpr.GetInstance(variables); 56 | return evaluator(listInstance, (int)indexExpr.Evaluate(variables)); 57 | } 58 | 59 | private static Func GenerateEvaluator() 60 | { 61 | return (obj, idx) => ((IList)obj)[idx]; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/ObjectMethodExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using Dahomey.ExpressionEvaluator.Expressions; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Reflection; 13 | 14 | namespace Dahomey.ExpressionEvaluator 15 | { 16 | public class ObjectMethodExpression : IObjectExpression 17 | { 18 | private IObjectExpression containingObject; 19 | private MethodInfo methodInfo; 20 | private ListExpression argumentsExpr; 21 | private Func, object, object> evaluator; 22 | 23 | public Type ObjectType { get; private set; } 24 | 25 | public ObjectMethodExpression(IObjectExpression containingObject, MethodInfo methodInfo, ListExpression argumentsExpr) 26 | { 27 | this.containingObject = containingObject; 28 | this.methodInfo = methodInfo; 29 | this.argumentsExpr = argumentsExpr; 30 | 31 | Type containingObjectType = containingObject.ObjectType; 32 | ObjectType = methodInfo.ReturnType; 33 | MethodInfo generateEvaluatorMethod; 34 | ParameterInfo[] parameters = methodInfo.GetParameters(); 35 | 36 | switch (parameters.Length) 37 | { 38 | case 0: 39 | generateEvaluatorMethod = GetType() 40 | .GetMethod("GenerateFunctor0", BindingFlags.NonPublic | BindingFlags.Static) 41 | .MakeGenericMethod(containingObjectType, ObjectType); 42 | break; 43 | 44 | case 1: 45 | generateEvaluatorMethod = GetType() 46 | .GetMethod("GenerateFunctor1", BindingFlags.NonPublic | BindingFlags.Static) 47 | .MakeGenericMethod(containingObjectType, parameters[0].ParameterType, ObjectType); 48 | break; 49 | 50 | case 2: 51 | generateEvaluatorMethod = GetType() 52 | .GetMethod("GenerateFunctor2", BindingFlags.NonPublic | BindingFlags.Static) 53 | .MakeGenericMethod(containingObjectType, 54 | parameters[0].ParameterType, 55 | parameters[1].ParameterType, 56 | ObjectType); 57 | break; 58 | 59 | case 3: 60 | generateEvaluatorMethod = GetType() 61 | .GetMethod("GenerateFunctor3", BindingFlags.NonPublic | BindingFlags.Static) 62 | .MakeGenericMethod(containingObjectType, 63 | parameters[0].ParameterType, 64 | parameters[1].ParameterType, 65 | parameters[2].ParameterType, 66 | ObjectType); 67 | break; 68 | 69 | default: 70 | throw new NotSupportedException(); 71 | } 72 | 73 | var generateEvaluatorDelegate = ReflectionHelper 74 | .CreateDelegate, object, object>>(generateEvaluatorMethod); 75 | 76 | evaluator = generateEvaluatorDelegate(methodInfo, argumentsExpr); 77 | } 78 | 79 | public object GetInstance(Dictionary variables) 80 | { 81 | object instance = containingObject.GetInstance(variables); 82 | return evaluator(variables, instance); 83 | } 84 | 85 | private static Func, object, object> GenerateFunctor0(MethodInfo methodInfo, ListExpression argumentsExpr) 86 | { 87 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 88 | 89 | return (variables, obj) => invoker((T)obj); 90 | } 91 | 92 | private static Func, object, object> GenerateFunctor1(MethodInfo methodInfo, ListExpression argumentsExpr) 93 | { 94 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 95 | Func, TP> argGetter = argumentsExpr.GetItemGetter(0); 96 | 97 | return (variables, obj) => invoker((T)obj, argGetter(variables)); 98 | } 99 | 100 | private static Func, object, object> GenerateFunctor2(MethodInfo methodInfo, ListExpression argumentsExpr) 101 | { 102 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 103 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 104 | Func, TP2> argGetter2 = argumentsExpr.GetItemGetter(1); 105 | 106 | return (variables, obj) => invoker((T)obj, argGetter1(variables), argGetter2(variables)); 107 | } 108 | 109 | private static Func, object, object> GenerateFunctor3(MethodInfo methodInfo, ListExpression argumentsExpr) 110 | { 111 | Func invoker = ReflectionHelper.CreateDelegate(methodInfo); 112 | Func, TP1> argGetter1 = argumentsExpr.GetItemGetter(0); 113 | Func, TP2> argGetter2 = argumentsExpr.GetItemGetter(1); 114 | Func, TP3> argGetter3 = argumentsExpr.GetItemGetter(2); 115 | 116 | return (variables, obj) => invoker((T)obj, argGetter1(variables), argGetter2(variables), argGetter3(variables)); 117 | } 118 | 119 | public override string ToString() 120 | { 121 | return string.Format("{0}.{1}({2})", containingObject, methodInfo.Name, argumentsExpr); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/ObjectPropertyExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Reflection; 12 | 13 | namespace Dahomey.ExpressionEvaluator 14 | { 15 | public class ObjectPropertyExpression : IObjectExpression 16 | { 17 | private IObjectExpression containingObject; 18 | private PropertyInfo propertyInfo; 19 | private readonly Func evaluator; 20 | 21 | public Type ObjectType { get; private set; } 22 | 23 | public ObjectPropertyExpression(IObjectExpression containingObject, PropertyInfo propertyInfo) 24 | { 25 | this.containingObject = containingObject; 26 | this.propertyInfo = propertyInfo; 27 | 28 | Type containingObjectType = containingObject.ObjectType; 29 | ObjectType = propertyInfo.PropertyType; 30 | 31 | MethodInfo generateEvaluatorMethod = GetType() 32 | .GetMethod("GenerateEvaluator", BindingFlags.NonPublic | BindingFlags.Static) 33 | .MakeGenericMethod(containingObjectType, ObjectType); 34 | 35 | Func> generateEvaluatorDelegate = 36 | ReflectionHelper.CreateDelegate>(generateEvaluatorMethod); 37 | 38 | evaluator = generateEvaluatorDelegate(propertyInfo); 39 | } 40 | 41 | public object GetInstance(Dictionary variables) 42 | { 43 | object containingObjectInstance = containingObject.GetInstance(variables); 44 | return evaluator(containingObjectInstance); 45 | } 46 | 47 | private static Func GenerateEvaluator(PropertyInfo propertyInfo) 48 | { 49 | Func propertyGetter = ReflectionHelper.CreateDelegate(propertyInfo); 50 | return obj => propertyGetter((T)obj); 51 | } 52 | 53 | public override string ToString() 54 | { 55 | return string.Format("{0}.{1}", containingObject, propertyInfo.Name); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/ObjectVariableExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | 12 | namespace Dahomey.ExpressionEvaluator 13 | { 14 | public class ObjectVariableExpression : IObjectExpression 15 | { 16 | private string variableName; 17 | public Type ObjectType { get; private set; } 18 | 19 | public ObjectVariableExpression(string variableName, Type variableType) 20 | { 21 | this.variableName = variableName; 22 | ObjectType = variableType; 23 | } 24 | 25 | public object GetInstance(Dictionary variables) 26 | { 27 | return variables[variableName]; 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return variableName; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/StringComparisonExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace Dahomey.ExpressionEvaluator 14 | { 15 | public class StrngComparisonExpression : IBooleanExpression 16 | { 17 | public Operator Operator { get; set; } 18 | public IStringExpression LeftExpr { get; set; } 19 | public IStringExpression RightExpr { get; set; } 20 | 21 | public bool Evaluate(Dictionary variables) 22 | { 23 | switch (Operator) 24 | { 25 | case Operator.Equal: 26 | return LeftExpr.Evaluate(variables) == RightExpr.Evaluate(variables); 27 | 28 | case Operator.NotEqual: 29 | return LeftExpr.Evaluate(variables) != RightExpr.Evaluate(variables); 30 | 31 | default: 32 | throw new NotSupportedException(string.Format("Operator {0} not supported", Operator)); 33 | } 34 | } 35 | 36 | public override string ToString() 37 | { 38 | StringBuilder sb = new StringBuilder(); 39 | 40 | if (LeftExpr != null) 41 | { 42 | sb.Append(LeftExpr).Append(' '); 43 | } 44 | 45 | if (RightExpr != null) 46 | { 47 | sb.Append(RightExpr).Append(' '); 48 | } 49 | 50 | sb.Append(Operator.PrettyPrint()); 51 | 52 | return sb.ToString(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Expressions/StringLiteralExpression.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System.Collections.Generic; 10 | using System.Globalization; 11 | 12 | namespace Dahomey.ExpressionEvaluator 13 | { 14 | public class StringLiteralExpression : IStringExpression 15 | { 16 | private string value; 17 | 18 | public StringLiteralExpression(string value) 19 | { 20 | this.value = value; 21 | } 22 | 23 | public string Evaluate(Dictionary variables) 24 | { 25 | return value; 26 | } 27 | 28 | public override string ToString() 29 | { 30 | return string.Format("\"{0}\"", value); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/Operator.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | 11 | namespace Dahomey.ExpressionEvaluator 12 | { 13 | public enum Operator 14 | { 15 | Mult, // * 16 | Div, // / 17 | Mod, // % 18 | Minus, // - 19 | Plus, // + 20 | LeftShift, // << 21 | RightShift, // >> 22 | LessThan, // < 23 | LessThanOrEqual, // <= 24 | GreaterThanOrEqual, // >= 25 | GreaterThan, // > 26 | Equal, // == 27 | NotEqual, // != 28 | BitwiseAnd, // & 29 | BitwiseXor, // ^ 30 | BitwiseOr, // | 31 | BitwiseComplement, // ~ 32 | Not, // ! 33 | And, // && 34 | Or, // || 35 | } 36 | 37 | internal static class OperatorExtension 38 | { 39 | public static string PrettyPrint(this Operator op) 40 | { 41 | switch (op) 42 | { 43 | case Operator.Mult: 44 | return "*"; 45 | 46 | case Operator.Div: 47 | return "/"; 48 | 49 | case Operator.Mod: 50 | return "%"; 51 | 52 | case Operator.Minus: 53 | return "-"; 54 | 55 | case Operator.Plus: 56 | return "+"; 57 | 58 | case Operator.LeftShift: 59 | return "<<"; 60 | 61 | case Operator.RightShift: 62 | return ">>"; 63 | 64 | case Operator.LessThan: 65 | return "<"; 66 | 67 | case Operator.LessThanOrEqual: 68 | return "<="; 69 | 70 | case Operator.GreaterThanOrEqual: 71 | return ">="; 72 | 73 | case Operator.GreaterThan: 74 | return ">"; 75 | 76 | case Operator.Equal: 77 | return "=="; 78 | 79 | case Operator.NotEqual: 80 | return "!="; 81 | 82 | case Operator.BitwiseAnd: 83 | return "&"; 84 | 85 | case Operator.BitwiseOr: 86 | return "|"; 87 | 88 | case Operator.BitwiseXor: 89 | return "^"; 90 | 91 | case Operator.BitwiseComplement: 92 | return "~"; 93 | 94 | case Operator.And: 95 | return "&&"; 96 | 97 | case Operator.Or: 98 | return "||"; 99 | 100 | case Operator.Not: 101 | return "!"; 102 | 103 | default: 104 | throw new NotSupportedException(string.Format("Operator {0} not supported", op)); 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Reflection; 13 | 14 | namespace Dahomey.ExpressionEvaluator 15 | { 16 | public static class ReflectionHelper 17 | { 18 | public static Func CreateDelegate(PropertyInfo propertyInfo) 19 | { 20 | return (Func)Delegate.CreateDelegate(typeof(Func), propertyInfo.GetGetMethod()); 21 | } 22 | 23 | public static Func CreateDelegate(MethodInfo methodInfo, object target = null) 24 | { 25 | return (Func)Delegate.CreateDelegate(typeof(Func), target, methodInfo); 26 | } 27 | 28 | public static Func CreateDelegate(MethodInfo methodInfo, object target = null) 29 | { 30 | return (Func)Delegate.CreateDelegate(typeof(Func), target, methodInfo); 31 | } 32 | 33 | public static Func CreateDelegate(MethodInfo methodInfo, object target = null) 34 | { 35 | return (Func)Delegate.CreateDelegate(typeof(Func), target, methodInfo); 36 | } 37 | 38 | public static Func CreateDelegate(MethodInfo methodInfo, object target = null) 39 | { 40 | return (Func)Delegate.CreateDelegate(typeof(Func), target, methodInfo); 41 | } 42 | 43 | public static Func CreateDelegate(MethodInfo methodInfo, object target = null) 44 | { 45 | return (Func)Delegate.CreateDelegate(typeof(Func), target, methodInfo); 46 | } 47 | 48 | public static bool IsList(Type type) 49 | { 50 | return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>); 51 | } 52 | 53 | public static bool IsNumberList(Type type) 54 | { 55 | Type itemType; 56 | 57 | if (type.IsArray) 58 | { 59 | itemType = type.GetElementType(); 60 | } 61 | else if (IsList(type)) 62 | { 63 | itemType = type.GetGenericArguments()[0]; 64 | } 65 | else 66 | { 67 | return false; 68 | } 69 | 70 | return IsNumber(itemType); 71 | } 72 | 73 | public static bool IsNumber(Type type) 74 | { 75 | return type == typeof(sbyte) 76 | || type == typeof(byte) 77 | || type == typeof(short) 78 | || type == typeof(ushort) 79 | || type == typeof(int) 80 | || type == typeof(uint) 81 | || type == typeof(long) 82 | || type == typeof(ulong) 83 | || type == typeof(float) 84 | || type == typeof(double) 85 | || type.IsEnum; 86 | } 87 | 88 | public static Type GetType(IEnumerable assemblies, string name) 89 | { 90 | return assemblies 91 | .SelectMany(a => a.GetTypes()) 92 | .FirstOrDefault(t => t.Name == name); 93 | } 94 | 95 | public static Func GenerateConverter() 96 | { 97 | string methodName; 98 | 99 | if (typeof(T).IsEnum) 100 | { 101 | // Hack: we force cast Func to Func 102 | methodName = "Int32ToDouble"; 103 | } 104 | else if (typeof(T) == typeof(sbyte)) 105 | { 106 | methodName = "SByteToDouble"; 107 | } 108 | else if (typeof(T) == typeof(byte)) 109 | { 110 | methodName = "ByteToDouble"; 111 | } 112 | else if (typeof(T) == typeof(short)) 113 | { 114 | methodName = "Int16ToDouble"; 115 | } 116 | else if (typeof(T) == typeof(ushort)) 117 | { 118 | methodName = "UInt16ToDouble"; 119 | } 120 | else if (typeof(T) == typeof(int)) 121 | { 122 | methodName = "Int32ToDouble"; 123 | } 124 | else if (typeof(T) == typeof(uint)) 125 | { 126 | methodName = "UInt32ToDouble"; 127 | } 128 | else if (typeof(T) == typeof(long)) 129 | { 130 | methodName = "Int64ToDouble"; 131 | } 132 | else if (typeof(T) == typeof(ulong)) 133 | { 134 | methodName = "UInt64ToDouble"; 135 | } 136 | else if (typeof(T) == typeof(float)) 137 | { 138 | methodName = "SingleToDouble"; 139 | } 140 | else if (typeof(T) == typeof(double)) 141 | { 142 | methodName = "DoubleToDouble"; 143 | } 144 | else 145 | { 146 | throw new NotSupportedException(string.Format("Cannot convert type {0} to double", typeof(T).Name)); 147 | } 148 | 149 | MethodInfo methodInfo = typeof(ReflectionHelper).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); 150 | return CreateDelegate(methodInfo); 151 | } 152 | 153 | public static Func GenerateFromDoubleConverter() 154 | { 155 | string methodName; 156 | 157 | if (typeof(T).IsEnum) 158 | { 159 | // Hack: we force cast Func to Func 160 | methodName = "DoubleToInt32"; 161 | } 162 | else if (typeof(T) == typeof(sbyte)) 163 | { 164 | methodName = "DoubleToSByte"; 165 | } 166 | else if (typeof(T) == typeof(byte)) 167 | { 168 | methodName = "DoubleToByte"; 169 | } 170 | else if (typeof(T) == typeof(short)) 171 | { 172 | methodName = "DoubleToInt16"; 173 | } 174 | else if (typeof(T) == typeof(ushort)) 175 | { 176 | methodName = "DoubleToUInt16"; 177 | } 178 | else if (typeof(T) == typeof(int)) 179 | { 180 | methodName = "DoubleToInt32"; 181 | } 182 | else if (typeof(T) == typeof(uint)) 183 | { 184 | methodName = "DoubleToUInt32"; 185 | } 186 | else if (typeof(T) == typeof(long)) 187 | { 188 | methodName = "DoubleToInt64"; 189 | } 190 | else if (typeof(T) == typeof(ulong)) 191 | { 192 | methodName = "DoubleToUInt64"; 193 | } 194 | else if (typeof(T) == typeof(float)) 195 | { 196 | methodName = "DoubleToSingle"; 197 | } 198 | else if (typeof(T) == typeof(double)) 199 | { 200 | methodName = "DoubleToDouble"; 201 | } 202 | else 203 | { 204 | throw new NotSupportedException(); 205 | } 206 | 207 | MethodInfo methodInfo = typeof(ReflectionHelper).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); 208 | return CreateDelegate(methodInfo); 209 | } 210 | 211 | private static double SByteToDouble(sbyte value) 212 | { 213 | return value; 214 | } 215 | 216 | private static double ByteToDouble(byte value) 217 | { 218 | return value; 219 | } 220 | 221 | private static double Int16ToDouble(short value) 222 | { 223 | return value; 224 | } 225 | 226 | private static double UInt16ToDouble(ushort value) 227 | { 228 | return value; 229 | } 230 | 231 | private static double Int32ToDouble(int value) 232 | { 233 | return value; 234 | } 235 | 236 | private static double UInt32ToDouble(uint value) 237 | { 238 | return value; 239 | } 240 | 241 | private static double Int64ToDouble(long value) 242 | { 243 | return value; 244 | } 245 | 246 | private static double UInt64ToDouble(ulong value) 247 | { 248 | return value; 249 | } 250 | 251 | private static double SingleToDouble(float value) 252 | { 253 | return value; 254 | } 255 | 256 | private static double DoubleToDouble(double value) 257 | { 258 | return value; 259 | } 260 | 261 | private static sbyte DoubleToSByte(double value) 262 | { 263 | return (sbyte)value; 264 | } 265 | 266 | private static byte DoubleToByte(double value) 267 | { 268 | return (byte)value; 269 | } 270 | 271 | private static short DoubleToInt16(double value) 272 | { 273 | return (short)value; 274 | } 275 | 276 | private static ushort DoubleToUInt16(double value) 277 | { 278 | return (ushort)value; 279 | } 280 | 281 | private static int DoubleToInt32(double value) 282 | { 283 | return (int)value; 284 | } 285 | 286 | private static uint DoubleToUInt32(double value) 287 | { 288 | return (uint)value; 289 | } 290 | 291 | private static long DoubleToInt64(double value) 292 | { 293 | return (long)value; 294 | } 295 | 296 | private static ulong DoubleToUInt64(double value) 297 | { 298 | return (ulong)value; 299 | } 300 | 301 | private static float DoubleToSingle(double value) 302 | { 303 | return (float)value; 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/SyntaxErrorException.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright © 2017, Dahomey Technologies and Contributors 4 | * For conditions of distribution and use, see copyright notice in license.txt file 5 | */ 6 | 7 | #endregion 8 | 9 | using System; 10 | 11 | namespace Dahomey.ExpressionEvaluator 12 | { 13 | public class SyntaxErrorException : Exception 14 | { 15 | public SyntaxErrorException() 16 | { 17 | } 18 | 19 | public SyntaxErrorException(string message) : base(message) 20 | { 21 | } 22 | 23 | public SyntaxErrorException(string message, Exception innerException) : base(message, innerException) 24 | { 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Dahomey.ExpressionEvaluator/grammar.txt: -------------------------------------------------------------------------------- 1 | // Define a grammar called Formula 2 | 3 | grammar Formula; 4 | 5 | // Tokens 6 | 7 | IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]*; 8 | NUMBER : ('0'| ([1-9][0-9]*)) ('.'[0-9]+)? (('e'|'E')('+'|'-')?[0-9]+)? ; 9 | TRUE : 'true'; 10 | FALSE: 'false'; 11 | WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines 12 | OPEN_BRACKET : '['; 13 | CLOSE_BRACKET : ']'; 14 | OPEN_PARENS : '('; 15 | CLOSE_PARENS : ')'; 16 | DOT : '.'; 17 | COMMA : ','; 18 | COLON : ':'; 19 | PLUS : '+'; 20 | MINUS : '-'; 21 | MULT : '*'; 22 | DIV : '/'; 23 | MOD : '%'; 24 | BITWISE_AND : '&'; 25 | BITWISE_OR : '|'; 26 | BITWISE_XOR : '^'; 27 | BITWISE_COMPLEMENT : '~'; 28 | INTERR : '?'; 29 | AND : '&&'; 30 | OR : '||'; 31 | NOT : '!'; 32 | LT : '<'; 33 | GT : '>'; 34 | EQ : '=='; 35 | NE : '!='; 36 | LE : '<='; 37 | GE : '>='; 38 | LEFT_SHIFT : '<<'; 39 | RIGHT_SHIFT : '>>'; 40 | DOUBLE_QUOTE : '"'; 41 | 42 | // Rules 43 | 44 | literal : TRUE | FALSE | NUMBER; 45 | primary_expression : parenthesized_expression | variable_expression | literal; 46 | parenthesized_expression : OPEN_PARENS expression CLOSE_PARENS; 47 | variable_expression : element_expression (DOT element_expression)*; 48 | element_expression : call_expression (bracket_expression)? 49 | call_expression : IDENTIFIER (invocation_expression)?; 50 | bracket_expression : OPEN_BRACKET expression CLOSE_BRACKET; 51 | invocation_expression : OPEN_PARENS expression_list? CLOSE_PARENS; 52 | expression_list : expression ( COMMA expression)*; 53 | unary_expression : primary_expression | (MINUS unary_expression) | (PLUS unary_expression) | (BITWISE_COMPLEMENT unary_expression) | (NOT unary_expression); 54 | multiplicative_expression : unary_expression ((MULT | DIV | MOD) unary_expression)*; 55 | additive_expression : multiplicative_expression ((PLUS | MINUS) multiplicative_expression)*; 56 | shift_expression : additive_expression ((LEFT_SHIFT | RIGHT_SHIFT) additive_expression)*; 57 | relational_expression : shift_expression ((LT | LE | GE | GT) shift_expression)?; 58 | equality_expression : relational_expression ((EQ | NE) relational_expression)?; 59 | bitwise_and_expression : equality_expression (BITWISE_AND equality_expression)*; 60 | bitwise_xor_expression : bitwise_and_expression (BITWISE_XOR bitwise_and_expression)*; 61 | bitwise_or_expression : bitwise_xor_expression (BITWISE_OR bitwise_xor_expression)*; 62 | logical_and_expression : bitwise_or_expression (AND bitwise_or_expression)*; 63 | logical_or_expression : logical_and_expression (OR logical_and_expression)*; 64 | conditional_expression : logical_or_expression (INTERR expression COLON expression)?; 65 | expression : conditional_expression; 66 | --------------------------------------------------------------------------------