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