Skip to content

Commit 1223df7

Browse files
authored
Merge pull request #4 from codingseb/dev
Dev
2 parents af59879 + 90fc7f4 commit 1223df7

File tree

3 files changed

+258
-168
lines changed

3 files changed

+258
-168
lines changed

ExpressionEvaluator.cs

Lines changed: 136 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Globalization;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Text.RegularExpressions;
8+
19
/// <summary>
210
/// This class allow to evaluate a string math or pseudo C# expression
311
/// </summary>
@@ -8,15 +16,19 @@ public class ExpressionEvaluator
816
private static Regex stringBeginningRegex = new Regex("^(?<interpolated>[$])?(?<escaped>[@])?[\"]");
917
private static Regex castRegex = new Regex(@"^\(\s*(?<typeName>[a-zA-Z_][a-zA-Z0-9_\.\[\]<>]*[?]?)\s*\)");
1018
private static Regex indexingBeginningRegex = new Regex(@"^[?]?\[");
11-
private static Regex primaryTypesRegex = new Regex(@"(?<=^|[^a-zA-Z_])(?<primaryType>object|string|bool[?]?|byte[?]?|char[?]?|decimal[?]?|double[?]?|short[?]?|int[?]?|long[?]?|sbyte[?]?|float[?]?|ushort[?]?|uint[?]?|void)(?=[^a-zA-Z_]|$)");
1219
private static Regex endOfStringWithDollar = new Regex("^[^\"{]*[\"{]");
1320
private static Regex endOfStringWithoutDollar = new Regex("^[^\"]*[\"]");
1421
private static Regex endOfStringInterpolationRegex = new Regex("^[^}\"]*[}\"]");
1522
private static Regex stringBeginningForEndBlockRegex = new Regex("[$]?[@]?[\"]$");
1623
private static Regex lambdaExpressionRegex = new Regex(@"^\s*(?<args>(\s*[(]\s*([a-zA-Z_][a-zA-Z0-9_]*\s*([,]\s*[a-zA-Z_][a-zA-Z0-9_]*\s*)*)?[)])|[a-zA-Z_][a-zA-Z0-9_]*)\s*=>(?<expression>.*)$");
1724
private static Regex lambdaArgRegex = new Regex(@"[a-zA-Z_][a-zA-Z0-9_]*");
1825

19-
private static Dictionary<string, Type> PrimaryTypesDict = new Dictionary<string, Type>()
26+
private static string instanceCreationWithNewKeywordRegexPattern = @"^new\s+(?<name>[a-zA-Z_][a-zA-Z0-9_.]*)\s*(?<isgeneric>[<](?>[^<>]+|(?<gentag>[<])|(?<-gentag>[>]))*(?(gentag)(?!))[>])?(?<isfunction>[(])?";
27+
private Regex instanceCreationWithNewKeywordRegex = new Regex(instanceCreationWithNewKeywordRegexPattern);
28+
private static string primaryTypesRegexPattern = @"(?<=^|[^a-zA-Z_])(?<primaryType>object|string|bool[?]?|byte[?]?|char[?]?|decimal[?]?|double[?]?|short[?]?|int[?]?|long[?]?|sbyte[?]?|float[?]?|ushort[?]?|uint[?]?|void)(?=[^a-zA-Z_]|$)";
29+
private Regex primaryTypesRegex = new Regex(primaryTypesRegexPattern);
30+
31+
private Dictionary<string, Type> primaryTypesDict = new Dictionary<string, Type>()
2032
{
2133
{ "object", typeof(object) },
2234
{ "string", typeof(string) },
@@ -117,7 +129,7 @@ private enum ExpressionOperator
117129
IndexingWithNullConditional,
118130
}
119131

120-
private static Dictionary<string, ExpressionOperator> operatorsDictionary = new Dictionary<string, ExpressionOperator>(StringComparer.OrdinalIgnoreCase)
132+
private Dictionary<string, ExpressionOperator> operatorsDictionary = new Dictionary<string, ExpressionOperator>(StringComparer.Ordinal)
121133
{
122134
{ "+", ExpressionOperator.Plus },
123135
{ "-", ExpressionOperator.Minus },
@@ -156,74 +168,74 @@ private enum ExpressionOperator
156168
private static List<Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>> operatorsEvaluations =
157169
new List<Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>>()
158170
{
159-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
160-
{
161-
{ExpressionOperator.Indexing, (dynamic left, dynamic right) => left[right] },
162-
{ExpressionOperator.IndexingWithNullConditional, (dynamic left, dynamic right) => left?[right] },
163-
},
164-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
165-
{
166-
{ExpressionOperator.UnaryPlus, (dynamic left, dynamic right) => +right },
167-
{ExpressionOperator.UnaryMinus, (dynamic left, dynamic right) => -right },
168-
{ExpressionOperator.LogicalNegation, (dynamic left, dynamic right) => !right },
169-
{ExpressionOperator.Cast, (dynamic left, dynamic right) => ChangeType(right, left) },
170-
},
171-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
172-
{
173-
{ExpressionOperator.Multiply, (dynamic left, dynamic right) => left * right },
174-
{ExpressionOperator.Divide, (dynamic left, dynamic right) => left / right },
175-
{ExpressionOperator.Modulo, (dynamic left, dynamic right) => left % right },
176-
},
177-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
178-
{
179-
{ExpressionOperator.Plus, (dynamic left, dynamic right) => left + right },
180-
{ExpressionOperator.Minus, (dynamic left, dynamic right) => left - right },
181-
},
182-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
183-
{
184-
{ExpressionOperator.ShiftBitsLeft, (dynamic left, dynamic right) => left << right },
185-
{ExpressionOperator.ShiftBitsRight, (dynamic left, dynamic right) => left >> right },
186-
},
187-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
188-
{
189-
{ExpressionOperator.Lower, (dynamic left, dynamic right) => left < right },
190-
{ExpressionOperator.Greater, (dynamic left, dynamic right) => left > right },
191-
{ExpressionOperator.LowerOrEqual, (dynamic left, dynamic right) => left <= right },
192-
{ExpressionOperator.GreaterOrEqual, (dynamic left, dynamic right) => left >= right },
193-
{ExpressionOperator.Is, (dynamic left, dynamic right) => left != null && (((ClassOrTypeName)right).Type).IsAssignableFrom(left.GetType()) },
194-
},
195-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
196-
{
197-
{ExpressionOperator.Equal, (dynamic left, dynamic right) => left == right },
198-
{ExpressionOperator.NotEqual, (dynamic left, dynamic right) => left != right },
199-
},
200-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
201-
{
202-
{ExpressionOperator.LogicalAnd, (dynamic left, dynamic right) => left & right },
203-
},
204-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
205-
{
206-
{ExpressionOperator.LogicalXor, (dynamic left, dynamic right) => left ^ right },
207-
},
208-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
209-
{
210-
{ExpressionOperator.LogicalOr, (dynamic left, dynamic right) => left | right },
211-
},
212-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
213-
{
214-
{ExpressionOperator.ConditionalAnd, (dynamic left, dynamic right) => left && right },
215-
},
216-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
217-
{
218-
{ExpressionOperator.ConditionalOr, (dynamic left, dynamic right) => left || right },
219-
},
220-
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
221-
{
222-
{ExpressionOperator.NullCoalescing, (dynamic left, dynamic right) => left ?? right },
223-
},
171+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
172+
{
173+
{ExpressionOperator.Indexing, (dynamic left, dynamic right) => left[right] },
174+
{ExpressionOperator.IndexingWithNullConditional, (dynamic left, dynamic right) => left?[right] },
175+
},
176+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
177+
{
178+
{ExpressionOperator.UnaryPlus, (dynamic left, dynamic right) => +right },
179+
{ExpressionOperator.UnaryMinus, (dynamic left, dynamic right) => -right },
180+
{ExpressionOperator.LogicalNegation, (dynamic left, dynamic right) => !right },
181+
{ExpressionOperator.Cast, (dynamic left, dynamic right) => ChangeType(right, left) },
182+
},
183+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
184+
{
185+
{ExpressionOperator.Multiply, (dynamic left, dynamic right) => left * right },
186+
{ExpressionOperator.Divide, (dynamic left, dynamic right) => left / right },
187+
{ExpressionOperator.Modulo, (dynamic left, dynamic right) => left % right },
188+
},
189+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
190+
{
191+
{ExpressionOperator.Plus, (dynamic left, dynamic right) => left + right },
192+
{ExpressionOperator.Minus, (dynamic left, dynamic right) => left - right },
193+
},
194+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
195+
{
196+
{ExpressionOperator.ShiftBitsLeft, (dynamic left, dynamic right) => left << right },
197+
{ExpressionOperator.ShiftBitsRight, (dynamic left, dynamic right) => left >> right },
198+
},
199+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
200+
{
201+
{ExpressionOperator.Lower, (dynamic left, dynamic right) => left < right },
202+
{ExpressionOperator.Greater, (dynamic left, dynamic right) => left > right },
203+
{ExpressionOperator.LowerOrEqual, (dynamic left, dynamic right) => left <= right },
204+
{ExpressionOperator.GreaterOrEqual, (dynamic left, dynamic right) => left >= right },
205+
{ExpressionOperator.Is, (dynamic left, dynamic right) => left != null && (((ClassOrTypeName)right).Type).IsAssignableFrom(left.GetType()) },
206+
},
207+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
208+
{
209+
{ExpressionOperator.Equal, (dynamic left, dynamic right) => left == right },
210+
{ExpressionOperator.NotEqual, (dynamic left, dynamic right) => left != right },
211+
},
212+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
213+
{
214+
{ExpressionOperator.LogicalAnd, (dynamic left, dynamic right) => left & right },
215+
},
216+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
217+
{
218+
{ExpressionOperator.LogicalXor, (dynamic left, dynamic right) => left ^ right },
219+
},
220+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
221+
{
222+
{ExpressionOperator.LogicalOr, (dynamic left, dynamic right) => left | right },
223+
},
224+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
225+
{
226+
{ExpressionOperator.ConditionalAnd, (dynamic left, dynamic right) => left && right },
227+
},
228+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
229+
{
230+
{ExpressionOperator.ConditionalOr, (dynamic left, dynamic right) => left || right },
231+
},
232+
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
233+
{
234+
{ExpressionOperator.NullCoalescing, (dynamic left, dynamic right) => left ?? right },
235+
},
224236
};
225237

226-
private static Dictionary<string, object> defaultVariables = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
238+
private Dictionary<string, object> defaultVariables = new Dictionary<string, object>(StringComparer.Ordinal)
227239
{
228240
{ "Pi", Math.PI },
229241
{ "E", Math.E },
@@ -232,7 +244,7 @@ private enum ExpressionOperator
232244
{ "false", false },
233245
};
234246

235-
private static Dictionary<string, Func<double, double>> simpleDoubleMathFuncsDictionary = new Dictionary<string, Func<double, double>>(StringComparer.OrdinalIgnoreCase)
247+
private Dictionary<string, Func<double, double>> simpleDoubleMathFuncsDictionary = new Dictionary<string, Func<double, double>>(StringComparer.Ordinal)
236248
{
237249
{ "Abs", Math.Abs },
238250
{ "Acos", Math.Acos },
@@ -252,15 +264,15 @@ private enum ExpressionOperator
252264
{ "Truncate", Math.Truncate },
253265
};
254266

255-
private static Dictionary<string, Func<double, double, double>> doubleDoubleMathFuncsDictionary = new Dictionary<string, Func<double, double, double>>(StringComparer.OrdinalIgnoreCase)
267+
private Dictionary<string, Func<double, double, double>> doubleDoubleMathFuncsDictionary = new Dictionary<string, Func<double, double, double>>(StringComparer.Ordinal)
256268
{
257269
{ "Atan2", Math.Atan2 },
258270
{ "IEEERemainder", Math.IEEERemainder },
259271
{ "Log", Math.Log },
260272
{ "Pow", Math.Pow },
261273
};
262274

263-
private static Dictionary<string, Func<ExpressionEvaluator, List<string>, object>> complexStandardFuncsDictionary = new Dictionary<string, Func<ExpressionEvaluator, List<string>, object>>(StringComparer.OrdinalIgnoreCase)
275+
private Dictionary<string, Func<ExpressionEvaluator, List<string>, object>> complexStandardFuncsDictionary = new Dictionary<string, Func<ExpressionEvaluator, List<string>, object>>(StringComparer.Ordinal)
264276
{
265277
{ "Array", (self, args) => args.ConvertAll(arg => self.Evaluate(arg)).ToArray() },
266278
{ "Avg", (self, args) => args.ConvertAll(arg => Convert.ToDouble(self.Evaluate(arg))).Sum() / args.Count },
@@ -345,10 +357,11 @@ private enum ExpressionOperator
345357
typeof(Enumerable) // For Linq extension methods
346358
};
347359

348-
private bool caseSensitiveEvaluation = false;
360+
private bool caseSensitiveEvaluation = true;
361+
349362
/// <summary>
350363
/// if true all evaluation are case sensitives, if false evaluations are case insensitive.
351-
/// By default = false
364+
/// By default = true
352365
/// </summary>
353366
public bool CaseSensitiveEvaluation
354367
{
@@ -357,14 +370,23 @@ public bool CaseSensitiveEvaluation
357370
{
358371
caseSensitiveEvaluation = value;
359372
Variables = Variables;
373+
primaryTypesDict = new Dictionary<string, Type>(primaryTypesDict, StringComparerForCasing);
360374
operatorsDictionary = new Dictionary<string, ExpressionOperator>(operatorsDictionary, StringComparerForCasing);
361375
defaultVariables = new Dictionary<string, object>(defaultVariables, StringComparerForCasing);
362376
simpleDoubleMathFuncsDictionary = new Dictionary<string, Func<double, double>>(simpleDoubleMathFuncsDictionary, StringComparerForCasing);
363377
doubleDoubleMathFuncsDictionary = new Dictionary<string, Func<double, double, double>>(doubleDoubleMathFuncsDictionary, StringComparerForCasing);
364378
complexStandardFuncsDictionary = new Dictionary<string, Func<ExpressionEvaluator, List<string>, object>>(complexStandardFuncsDictionary, StringComparerForCasing);
379+
instanceCreationWithNewKeywordRegex = new Regex(instanceCreationWithNewKeywordRegexPattern, caseSensitiveEvaluation ? RegexOptions.None : RegexOptions.IgnoreCase);
380+
primaryTypesRegex = new Regex(primaryTypesRegexPattern, caseSensitiveEvaluation ? RegexOptions.None : RegexOptions.IgnoreCase);
365381
}
366382
}
367383

384+
/// <summary>
385+
/// if true allow to add the prefix Fluid or Fluent before void methods names to return back the instance on which the method is call.
386+
/// if false unactive this functionality.
387+
/// </summary>
388+
public bool FluidPrefixingActive { get; set; } = true;
389+
368390
private StringComparer StringComparerForCasing
369391
{
370392
get
@@ -399,7 +421,7 @@ public BindingFlags StaticBindingFlag
399421
}
400422
}
401423

402-
private Dictionary<string, object> variables = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
424+
private Dictionary<string, object> variables = new Dictionary<string, object>(StringComparer.Ordinal);
403425

404426
/// <summary>
405427
/// The Values of the variable use in the expressions
@@ -467,6 +489,7 @@ public object Evaluate(string expr)
467489

468490
if (!(EvaluateCast(restOfExpression, stack, ref i)
469491
|| EvaluateNumber(restOfExpression, stack, ref i)
492+
|| EvaluateInstanceCreationWithNewKeyword(expr, restOfExpression,stack, ref i)
470493
|| EvaluateVarOrFunc(expr, restOfExpression, stack, ref i)
471494
|| EvaluateTwoCharsOperators(expr, stack, ref i)))
472495
{
@@ -585,6 +608,36 @@ private bool EvaluateNumber(string restOfExpression, Stack<object> stack, ref in
585608
}
586609
}
587610

611+
private bool EvaluateInstanceCreationWithNewKeyword(string expr, string restOfExpression, Stack<object> stack, ref int i)
612+
{
613+
Match instanceCreationMatch = instanceCreationWithNewKeywordRegex.Match(restOfExpression);
614+
615+
if(instanceCreationMatch.Success &&
616+
(stack.Count == 0
617+
|| stack.Peek() is ExpressionOperator))
618+
{
619+
string completeName = instanceCreationMatch.Groups["name"].Value;
620+
621+
i += instanceCreationMatch.Length;
622+
623+
if (!instanceCreationMatch.Groups["isfunction"].Success)
624+
throw new ExpressionEvaluatorSyntaxErrorException($"No '(' found after {instanceCreationMatch.Value}");
625+
626+
List<string> constructorArgs = GetExpressionsBetweenParenthis(expr, ref i, true);
627+
628+
Type type = GetTypeByFriendlyName(completeName, true);
629+
630+
List<object> cArgs = constructorArgs.ConvertAll(arg => Evaluate(arg));
631+
stack.Push(Activator.CreateInstance(type, cArgs.ToArray()));
632+
633+
return true;
634+
}
635+
else
636+
{
637+
return false;
638+
}
639+
}
640+
588641
private bool EvaluateVarOrFunc(string expr, string restOfExpression, Stack<object> stack, ref int i)
589642
{
590643
Match varFuncMatch = varOrFunctionRegEx.Match(restOfExpression);
@@ -1119,7 +1172,9 @@ private MethodInfo GetRealMethod(ref Type type, ref object obj, string func, Bin
11191172
MethodInfo methodInfo = null;
11201173
List<object> modifiedArgs = new List<object>(args);
11211174

1122-
if (func.ManageCasing(CaseSensitiveEvaluation).StartsWith("Fluid".ManageCasing(CaseSensitiveEvaluation)) || func.ManageCasing(CaseSensitiveEvaluation).StartsWith("Fluent".ManageCasing(CaseSensitiveEvaluation)))
1175+
if (FluidPrefixingActive &&
1176+
(func.ManageCasing(CaseSensitiveEvaluation).StartsWith("Fluid".ManageCasing(CaseSensitiveEvaluation))
1177+
|| func.ManageCasing(CaseSensitiveEvaluation).StartsWith("Fluent".ManageCasing(CaseSensitiveEvaluation))))
11231178
{
11241179
methodInfo = GetRealMethod(ref type, ref obj, func.ManageCasing(CaseSensitiveEvaluation).Substring(func.ManageCasing(CaseSensitiveEvaluation).StartsWith("Fluid".ManageCasing(CaseSensitiveEvaluation)) ? 5 : 6), flag, modifiedArgs);
11251180
if (methodInfo != null)
@@ -1329,7 +1384,7 @@ private bool DefaultFunctions(string name, List<string> args, out object result)
13291384
return functionExists;
13301385
}
13311386

1332-
private Type GetTypeByFriendlyName(string typeName)
1387+
private Type GetTypeByFriendlyName(string typeName, bool tryWithNamespaceInclude = false)
13331388
{
13341389
Type result = null;
13351390
try
@@ -1340,7 +1395,7 @@ private Type GetTypeByFriendlyName(string typeName)
13401395
{
13411396
typeName = primaryTypesRegex.Replace(typeName, delegate (Match match)
13421397
{
1343-
return PrimaryTypesDict[match.Value].ToString();
1398+
return primaryTypesDict[match.Value].ToString();
13441399
});
13451400

13461401
result = Type.GetType(typeName, false, true);
@@ -1353,6 +1408,9 @@ private Type GetTypeByFriendlyName(string typeName)
13531408

13541409
for (int a = 0; a < Assemblies.Count && result == null; a++)
13551410
{
1411+
if (tryWithNamespaceInclude)
1412+
result = Type.GetType($"{typeName},{Assemblies[a].FullName}", false, true);
1413+
13561414
for (int i = 0; i < Namespaces.Count && result == null; i++)
13571415
{
13581416
result = Type.GetType($"{Namespaces[i]}.{typeName},{Assemblies[a].FullName}", false, true);

0 commit comments

Comments
 (0)