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
+
1
9
/// <summary>
2
10
/// This class allow to evaluate a string math or pseudo C# expression
3
11
/// </summary>
@@ -8,15 +16,19 @@ public class ExpressionEvaluator
8
16
private static Regex stringBeginningRegex = new Regex ( "^(?<interpolated>[$])?(?<escaped>[@])?[\" ]" ) ;
9
17
private static Regex castRegex = new Regex ( @"^\(\s*(?<typeName>[a-zA-Z_][a-zA-Z0-9_\.\[\]<>]*[?]?)\s*\)" ) ;
10
18
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_]|$)" ) ;
12
19
private static Regex endOfStringWithDollar = new Regex ( "^[^\" {]*[\" {]" ) ;
13
20
private static Regex endOfStringWithoutDollar = new Regex ( "^[^\" ]*[\" ]" ) ;
14
21
private static Regex endOfStringInterpolationRegex = new Regex ( "^[^}\" ]*[}\" ]" ) ;
15
22
private static Regex stringBeginningForEndBlockRegex = new Regex ( "[$]?[@]?[\" ]$" ) ;
16
23
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>.*)$" ) ;
17
24
private static Regex lambdaArgRegex = new Regex ( @"[a-zA-Z_][a-zA-Z0-9_]*" ) ;
18
25
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 > ( )
20
32
{
21
33
{ "object" , typeof ( object ) } ,
22
34
{ "string" , typeof ( string ) } ,
@@ -117,7 +129,7 @@ private enum ExpressionOperator
117
129
IndexingWithNullConditional ,
118
130
}
119
131
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 )
121
133
{
122
134
{ "+" , ExpressionOperator . Plus } ,
123
135
{ "-" , ExpressionOperator . Minus } ,
@@ -156,74 +168,74 @@ private enum ExpressionOperator
156
168
private static List < Dictionary < ExpressionOperator , Func < dynamic , dynamic , object > > > operatorsEvaluations =
157
169
new List < Dictionary < ExpressionOperator , Func < dynamic , dynamic , object > > > ( )
158
170
{
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
+ } ,
224
236
} ;
225
237
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 )
227
239
{
228
240
{ "Pi" , Math . PI } ,
229
241
{ "E" , Math . E } ,
@@ -232,7 +244,7 @@ private enum ExpressionOperator
232
244
{ "false" , false } ,
233
245
} ;
234
246
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 )
236
248
{
237
249
{ "Abs" , Math . Abs } ,
238
250
{ "Acos" , Math . Acos } ,
@@ -252,15 +264,15 @@ private enum ExpressionOperator
252
264
{ "Truncate" , Math . Truncate } ,
253
265
} ;
254
266
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 )
256
268
{
257
269
{ "Atan2" , Math . Atan2 } ,
258
270
{ "IEEERemainder" , Math . IEEERemainder } ,
259
271
{ "Log" , Math . Log } ,
260
272
{ "Pow" , Math . Pow } ,
261
273
} ;
262
274
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 )
264
276
{
265
277
{ "Array" , ( self , args ) => args . ConvertAll ( arg => self . Evaluate ( arg ) ) . ToArray ( ) } ,
266
278
{ "Avg" , ( self , args ) => args . ConvertAll ( arg => Convert . ToDouble ( self . Evaluate ( arg ) ) ) . Sum ( ) / args . Count } ,
@@ -345,10 +357,11 @@ private enum ExpressionOperator
345
357
typeof ( Enumerable ) // For Linq extension methods
346
358
} ;
347
359
348
- private bool caseSensitiveEvaluation = false ;
360
+ private bool caseSensitiveEvaluation = true ;
361
+
349
362
/// <summary>
350
363
/// if true all evaluation are case sensitives, if false evaluations are case insensitive.
351
- /// By default = false
364
+ /// By default = true
352
365
/// </summary>
353
366
public bool CaseSensitiveEvaluation
354
367
{
@@ -357,14 +370,23 @@ public bool CaseSensitiveEvaluation
357
370
{
358
371
caseSensitiveEvaluation = value ;
359
372
Variables = Variables ;
373
+ primaryTypesDict = new Dictionary < string , Type > ( primaryTypesDict , StringComparerForCasing ) ;
360
374
operatorsDictionary = new Dictionary < string , ExpressionOperator > ( operatorsDictionary , StringComparerForCasing ) ;
361
375
defaultVariables = new Dictionary < string , object > ( defaultVariables , StringComparerForCasing ) ;
362
376
simpleDoubleMathFuncsDictionary = new Dictionary < string , Func < double , double > > ( simpleDoubleMathFuncsDictionary , StringComparerForCasing ) ;
363
377
doubleDoubleMathFuncsDictionary = new Dictionary < string , Func < double , double , double > > ( doubleDoubleMathFuncsDictionary , StringComparerForCasing ) ;
364
378
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 ) ;
365
381
}
366
382
}
367
383
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
+
368
390
private StringComparer StringComparerForCasing
369
391
{
370
392
get
@@ -399,7 +421,7 @@ public BindingFlags StaticBindingFlag
399
421
}
400
422
}
401
423
402
- private Dictionary < string , object > variables = new Dictionary < string , object > ( StringComparer . OrdinalIgnoreCase ) ;
424
+ private Dictionary < string , object > variables = new Dictionary < string , object > ( StringComparer . Ordinal ) ;
403
425
404
426
/// <summary>
405
427
/// The Values of the variable use in the expressions
@@ -467,6 +489,7 @@ public object Evaluate(string expr)
467
489
468
490
if ( ! ( EvaluateCast ( restOfExpression , stack , ref i )
469
491
|| EvaluateNumber ( restOfExpression , stack , ref i )
492
+ || EvaluateInstanceCreationWithNewKeyword ( expr , restOfExpression , stack , ref i )
470
493
|| EvaluateVarOrFunc ( expr , restOfExpression , stack , ref i )
471
494
|| EvaluateTwoCharsOperators ( expr , stack , ref i ) ) )
472
495
{
@@ -585,6 +608,36 @@ private bool EvaluateNumber(string restOfExpression, Stack<object> stack, ref in
585
608
}
586
609
}
587
610
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
+
588
641
private bool EvaluateVarOrFunc ( string expr , string restOfExpression , Stack < object > stack , ref int i )
589
642
{
590
643
Match varFuncMatch = varOrFunctionRegEx . Match ( restOfExpression ) ;
@@ -1119,7 +1172,9 @@ private MethodInfo GetRealMethod(ref Type type, ref object obj, string func, Bin
1119
1172
MethodInfo methodInfo = null ;
1120
1173
List < object > modifiedArgs = new List < object > ( args ) ;
1121
1174
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 ) ) ) )
1123
1178
{
1124
1179
methodInfo = GetRealMethod ( ref type , ref obj , func . ManageCasing ( CaseSensitiveEvaluation ) . Substring ( func . ManageCasing ( CaseSensitiveEvaluation ) . StartsWith ( "Fluid" . ManageCasing ( CaseSensitiveEvaluation ) ) ? 5 : 6 ) , flag , modifiedArgs ) ;
1125
1180
if ( methodInfo != null )
@@ -1329,7 +1384,7 @@ private bool DefaultFunctions(string name, List<string> args, out object result)
1329
1384
return functionExists ;
1330
1385
}
1331
1386
1332
- private Type GetTypeByFriendlyName ( string typeName )
1387
+ private Type GetTypeByFriendlyName ( string typeName , bool tryWithNamespaceInclude = false )
1333
1388
{
1334
1389
Type result = null ;
1335
1390
try
@@ -1340,7 +1395,7 @@ private Type GetTypeByFriendlyName(string typeName)
1340
1395
{
1341
1396
typeName = primaryTypesRegex . Replace ( typeName , delegate ( Match match )
1342
1397
{
1343
- return PrimaryTypesDict [ match . Value ] . ToString ( ) ;
1398
+ return primaryTypesDict [ match . Value ] . ToString ( ) ;
1344
1399
} ) ;
1345
1400
1346
1401
result = Type . GetType ( typeName , false , true ) ;
@@ -1353,6 +1408,9 @@ private Type GetTypeByFriendlyName(string typeName)
1353
1408
1354
1409
for ( int a = 0 ; a < Assemblies . Count && result == null ; a ++ )
1355
1410
{
1411
+ if ( tryWithNamespaceInclude )
1412
+ result = Type . GetType ( $ "{ typeName } ,{ Assemblies [ a ] . FullName } ", false , true ) ;
1413
+
1356
1414
for ( int i = 0 ; i < Namespaces . Count && result == null ; i ++ )
1357
1415
{
1358
1416
result = Type . GetType ( $ "{ Namespaces [ i ] } .{ typeName } ,{ Assemblies [ a ] . FullName } ", false , true ) ;
0 commit comments