@@ -38,7 +38,7 @@ public partial class ExpressionEvaluator
38
38
protected static readonly Regex otherBasesNumberRegex = new Regex ( "^(?<sign>[+-])?(?<value>0(?<type>x)([0-9a-f][0-9a-f_]*[0-9a-f]|[0-9a-f])|0(?<type>b)([01][01_]*[01]|[01]))" , RegexOptions . IgnoreCase | RegexOptions . Compiled ) ;
39
39
protected static readonly Regex stringBeginningRegex = new Regex ( "^(?<interpolated>[$])?(?<escaped>[@])?[\" ]" , RegexOptions . Compiled ) ;
40
40
protected static readonly Regex internalCharRegex = new Regex ( @"^['](\\[\\'0abfnrtv]|[^'])[']" , RegexOptions . Compiled ) ;
41
- protected static readonly Regex indexingBeginningRegex = new Regex ( @"^[?]?\[" , RegexOptions . Compiled ) ;
41
+ protected static readonly Regex indexingBeginningRegex = new Regex ( @"^(?<nullConditional> [?]) ?\[" , RegexOptions . Compiled ) ;
42
42
protected static readonly Regex arrayTypeDetectionRegex = new Regex ( @"^(\s*(\[(?>(?>\s+)|[,])*)\])+" , RegexOptions . Compiled ) ;
43
43
protected static readonly Regex assignationOrPostFixOperatorRegex = new Regex ( @"^(?>\s*)((?<assignmentPrefix>[+\-*/%&|^]|<<|>>|\?\?)?=(?![=>])|(?<postfixOperator>([+][+]|--)(?![\p{L}_0-9])))" ) ;
44
44
protected static readonly Regex genericsDecodeRegex = new Regex ( "(?<name>[^,<>]+)(?<isgeneric>[<](?>[^<>]+|(?<gentag>[<])|(?<-gentag>[>]))*(?(gentag)(?!))[>])?" , RegexOptions . Compiled ) ;
@@ -229,45 +229,9 @@ protected enum TryBlockEvaluatedState
229
229
protected virtual IList < ExpressionOperator > RightOperandOnlyOperatorsEvaluationDictionary => rightOperandOnlyOperatorsEvaluationDictionary ;
230
230
protected virtual IList < IDictionary < ExpressionOperator , Func < dynamic , dynamic , object > > > OperatorsEvaluations => operatorsEvaluations ;
231
231
232
- protected static object IndexingOperatorFunc ( dynamic left , dynamic right )
233
- {
234
- if ( left is NullConditionalNullValue )
235
- {
236
- return left ;
237
- }
238
- else if ( left is BubbleExceptionContainer )
239
- {
240
- return left ;
241
- }
242
- Type type = ( ( object ) left ) . GetType ( ) ;
243
-
244
- if ( left is IDictionary < string , object > dictionaryLeft )
245
- {
246
- return dictionaryLeft [ right ] ;
247
- }
248
- else if ( type . GetMethod ( "Item" , new Type [ ] { ( ( object ) right ) . GetType ( ) } ) is MethodInfo methodInfo )
249
- {
250
- return methodInfo . Invoke ( left , new object [ ] { right } ) ;
251
- }
252
-
253
- return left [ right ] ;
254
- }
255
-
256
232
protected static readonly IList < IDictionary < ExpressionOperator , Func < dynamic , dynamic , object > > > operatorsEvaluations =
257
233
new List < IDictionary < ExpressionOperator , Func < dynamic , dynamic , object > > > ( )
258
234
{
259
- new Dictionary < ExpressionOperator , Func < dynamic , dynamic , object > > ( )
260
- {
261
- { ExpressionOperator . Indexing , IndexingOperatorFunc } ,
262
- { ExpressionOperator . IndexingWithNullConditional , ( dynamic left , dynamic right ) =>
263
- {
264
- if ( left == null )
265
- return new NullConditionalNullValue ( ) ;
266
-
267
- return IndexingOperatorFunc ( left , right ) ;
268
- }
269
- } ,
270
- } ,
271
235
new Dictionary < ExpressionOperator , Func < dynamic , dynamic , object > > ( )
272
236
{
273
237
{ ExpressionOperator . UnaryPlus , ( dynamic _ , dynamic right ) => + right } ,
@@ -2779,54 +2743,24 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
2779
2743
2780
2744
if ( indexingBeginningMatch . Success )
2781
2745
{
2782
- StringBuilder innerExp = new StringBuilder ( ) ;
2783
2746
i += indexingBeginningMatch . Length ;
2784
- int bracketCount = 1 ;
2785
- for ( ; i < expression . Length ; i ++ )
2786
- {
2787
- Match internalStringMatch = stringBeginningRegex . Match ( expression . Substring ( i ) ) ;
2788
-
2789
- if ( internalStringMatch . Success )
2790
- {
2791
- string innerString = internalStringMatch . Value + GetCodeUntilEndOfString ( expression . Substring ( i + internalStringMatch . Length ) , internalStringMatch ) ;
2792
- innerExp . Append ( innerString ) ;
2793
- i += innerString . Length - 1 ;
2794
- }
2795
- else
2796
- {
2797
- string s = expression . Substring ( i , 1 ) ;
2798
-
2799
- if ( s . Equals ( "[" ) ) bracketCount ++ ;
2800
-
2801
- if ( s . Equals ( "]" ) )
2802
- {
2803
- bracketCount -- ;
2804
- if ( bracketCount == 0 ) break ;
2805
- }
2806
- innerExp . Append ( s ) ;
2807
- }
2808
- }
2809
-
2810
- if ( bracketCount > 0 )
2811
- {
2812
- string beVerb = bracketCount == 1 ? "is" : "are" ;
2813
- throw new Exception ( $ "{ bracketCount } ']' character { beVerb } missing in expression : [{ expression } ]") ;
2814
- }
2815
2747
2816
2748
dynamic left = stack . Pop ( ) ;
2817
2749
2818
- if ( left is NullConditionalNullValue )
2750
+ List < string > indexingArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expression , ref i , true , startChar : "[" , endChar : "]" ) ;
2751
+
2752
+ if ( left is NullConditionalNullValue || left is BubbleExceptionContainer )
2819
2753
{
2820
2754
stack . Push ( left ) ;
2821
2755
return true ;
2822
2756
}
2823
- else if ( left is BubbleExceptionContainer )
2757
+ else if ( indexingBeginningMatch . Groups [ "nullConditional" ] . Success && left == null )
2824
2758
{
2825
- stack . Push ( left ) ;
2759
+ stack . Push ( new NullConditionalNullValue ( ) ) ;
2826
2760
return true ;
2827
2761
}
2828
2762
2829
- IndexingPreEvaluationEventArg indexingPreEvaluationEventArg = new IndexingPreEvaluationEventArg ( innerExp . ToString ( ) , this , left ) ;
2763
+ IndexingPreEvaluationEventArg indexingPreEvaluationEventArg = new IndexingPreEvaluationEventArg ( indexingArgs , this , left ) ;
2830
2764
2831
2765
PreEvaluateIndexing ? . Invoke ( this , indexingPreEvaluationEventArg ) ;
2832
2766
@@ -2840,15 +2774,67 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
2840
2774
}
2841
2775
else
2842
2776
{
2843
- dynamic right = Evaluate ( innerExp . ToString ( ) ) ;
2844
- ExpressionOperator op = indexingBeginningMatch . Length == 2 ? ExpressionOperator . IndexingWithNullConditional : ExpressionOperator . Indexing ;
2777
+ Match assignationOrPostFixOperatorMatch = null ;
2778
+ dynamic valueToPush = null ;
2779
+ List < dynamic > oIndexingArgs = indexingArgs . ConvertAll ( Evaluate ) ;
2780
+ PropertyInfo [ ] itemProperties = null ;
2845
2781
2846
- if ( OptionForceIntegerNumbersEvaluationsAsDoubleByDefault && right is double && Regex . IsMatch ( innerExp . ToString ( ) , @"^\d+$" ) )
2847
- right = ( int ) right ;
2782
+ if ( ! ( left is IDictionary < string , object > ) )
2783
+ {
2784
+ Type type = ( ( object ) left ) . GetType ( ) ;
2848
2785
2849
- Match assignationOrPostFixOperatorMatch = null ;
2786
+ if ( type . IsArray && OptionForceIntegerNumbersEvaluationsAsDoubleByDefault )
2787
+ {
2788
+ oIndexingArgs = oIndexingArgs . Select ( o => o is double ? ( int ) o : o ) . ToList ( ) ;
2789
+ }
2790
+ else
2791
+ {
2792
+ itemProperties = type . GetProperties ( )
2793
+ . Where ( property => property . GetIndexParameters ( ) . Length > 0
2794
+ && property . GetIndexParameters ( ) . Length == oIndexingArgs . Count
2795
+ && property . GetIndexParameters ( ) . All ( parameter => parameter . ParameterType . IsAssignableFrom ( oIndexingArgs [ parameter . Position ] . GetType ( ) ) ) )
2796
+ . ToArray ( ) ;
2850
2797
2851
- dynamic valueToPush = null ;
2798
+ if ( itemProperties . Length == 0 && OptionForceIntegerNumbersEvaluationsAsDoubleByDefault )
2799
+ {
2800
+ List < dynamic > backupIndexedArgs = oIndexingArgs . ToList ( ) ;
2801
+
2802
+ itemProperties = type . GetProperties ( )
2803
+ . Where ( property => property . Name . Equals ( "Item" )
2804
+ && property . GetIndexParameters ( ) . Length == oIndexingArgs . Count
2805
+ && property . GetIndexParameters ( ) . All ( parameter =>
2806
+ {
2807
+ if ( parameter . ParameterType . IsAssignableFrom ( ( ( object ) oIndexingArgs [ parameter . Position ] ) . GetType ( ) ) )
2808
+ {
2809
+ return true ;
2810
+ }
2811
+ else if ( parameter . ParameterType == typeof ( int ) && oIndexingArgs [ parameter . Position ] is double )
2812
+ {
2813
+ oIndexingArgs [ parameter . Position ] = ( int ) oIndexingArgs [ parameter . Position ] ;
2814
+ return true ;
2815
+ }
2816
+ else
2817
+ {
2818
+ return false ;
2819
+ }
2820
+ } ) )
2821
+ . ToArray ( ) ;
2822
+
2823
+ if ( itemProperties . Length == 0 )
2824
+ oIndexingArgs = backupIndexedArgs ;
2825
+ }
2826
+ }
2827
+ }
2828
+
2829
+ object GetIndexedObject ( )
2830
+ {
2831
+ if ( left is IDictionary < string , object > dictionaryLeft )
2832
+ return dictionaryLeft [ oIndexingArgs [ 0 ] ] ;
2833
+ else if ( itemProperties ? . Length > 0 )
2834
+ return itemProperties [ 0 ] . GetValue ( left , oIndexingArgs . Cast < object > ( ) . ToArray ( ) ) ;
2835
+ else
2836
+ return left [ oIndexingArgs [ 0 ] ] ;
2837
+ }
2852
2838
2853
2839
if ( OptionIndexingAssignationActive && ( assignationOrPostFixOperatorMatch = assignationOrPostFixOperatorRegex . Match ( expression . Substring ( i + 1 ) ) ) . Success )
2854
2840
{
@@ -2860,31 +2846,42 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
2860
2846
if ( stack . Count > 1 )
2861
2847
throw new ExpressionEvaluatorSyntaxErrorException ( $ "The left part of { exceptionContext } must be a variable, a property or an indexer.") ;
2862
2848
2863
- if ( op == ExpressionOperator . IndexingWithNullConditional )
2849
+ if ( indexingBeginningMatch . Groups [ "nullConditional" ] . Success )
2864
2850
throw new ExpressionEvaluatorSyntaxErrorException ( $ "Null conditional is not usable left to { exceptionContext } ") ;
2865
2851
2866
2852
if ( postFixOperator )
2867
2853
{
2868
2854
if ( left is IDictionary < string , object > dictionaryLeft )
2869
- valueToPush = assignationOrPostFixOperatorMatch . Groups [ "postfixOperator" ] . Value . Equals ( "++" ) ? dictionaryLeft [ right ] ++ : dictionaryLeft [ right ] -- ;
2855
+ {
2856
+ valueToPush = assignationOrPostFixOperatorMatch . Groups [ "postfixOperator" ] . Value . Equals ( "++" ) ? dictionaryLeft [ oIndexingArgs [ 0 ] ] ++ : dictionaryLeft [ oIndexingArgs [ 0 ] ] -- ;
2857
+ }
2858
+ else if ( itemProperties ? . Length > 0 )
2859
+ {
2860
+ valueToPush = itemProperties [ 0 ] . GetValue ( left , oIndexingArgs . Cast < object > ( ) . ToArray ( ) ) ;
2861
+ itemProperties [ 0 ] . SetValue ( left , assignationOrPostFixOperatorMatch . Groups [ "postfixOperator" ] . Value . Equals ( "++" ) ? valueToPush + 1 : valueToPush - 1 , oIndexingArgs . Cast < object > ( ) . ToArray ( ) ) ;
2862
+ }
2870
2863
else
2871
- valueToPush = assignationOrPostFixOperatorMatch . Groups [ "postfixOperator" ] . Value . Equals ( "++" ) ? left [ right ] ++ : left [ right ] -- ;
2864
+ {
2865
+ valueToPush = assignationOrPostFixOperatorMatch . Groups [ "postfixOperator" ] . Value . Equals ( "++" ) ? left [ oIndexingArgs [ 0 ] ] ++ : left [ oIndexingArgs [ 0 ] ] -- ;
2866
+ }
2872
2867
}
2873
2868
else
2874
2869
{
2875
- valueToPush = ManageKindOfAssignation ( expression , ref i , assignationOrPostFixOperatorMatch , ( ) => OperatorsEvaluations [ 0 ] [ op ] ( left , right ) ) ;
2870
+ valueToPush = ManageKindOfAssignation ( expression , ref i , assignationOrPostFixOperatorMatch , GetIndexedObject ) ;
2876
2871
2877
2872
if ( left is IDictionary < string , object > dictionaryLeft )
2878
- dictionaryLeft [ right ] = valueToPush ;
2873
+ dictionaryLeft [ oIndexingArgs [ 0 ] ] = valueToPush ;
2874
+ else if ( itemProperties ? . Length > 0 )
2875
+ itemProperties [ 0 ] . SetValue ( left , valueToPush , oIndexingArgs . Cast < object > ( ) . ToArray ( ) ) ;
2879
2876
else
2880
- left [ right ] = valueToPush ;
2877
+ left [ oIndexingArgs [ 0 ] ] = valueToPush ;
2881
2878
2882
2879
stack . Clear ( ) ;
2883
2880
}
2884
2881
}
2885
2882
else
2886
2883
{
2887
- valueToPush = OperatorsEvaluations [ 0 ] [ op ] ( left , right ) ;
2884
+ valueToPush = GetIndexedObject ( ) ;
2888
2885
}
2889
2886
2890
2887
stack . Push ( valueToPush ) ;
@@ -4391,8 +4388,6 @@ protected ExpressionOperator()
4391
4388
public static readonly ExpressionOperator ShiftBitsRight = new ExpressionOperator ( ) ;
4392
4389
public static readonly ExpressionOperator NullCoalescing = new ExpressionOperator ( ) ;
4393
4390
public static readonly ExpressionOperator Cast = new ExpressionOperator ( ) ;
4394
- public static readonly ExpressionOperator Indexing = new ExpressionOperator ( ) ;
4395
- public static readonly ExpressionOperator IndexingWithNullConditional = new ExpressionOperator ( ) ;
4396
4391
4397
4392
public override bool Equals ( object obj )
4398
4393
{
@@ -4820,17 +4815,17 @@ public FunctionPreEvaluationEventArg(string name, List<string> args = null, Expr
4820
4815
/// </summary>
4821
4816
public partial class IndexingPreEvaluationEventArg : EventArgs
4822
4817
{
4823
- public IndexingPreEvaluationEventArg ( string arg , ExpressionEvaluator evaluator , object onInstance )
4818
+ public IndexingPreEvaluationEventArg ( List < string > args , ExpressionEvaluator evaluator , object onInstance )
4824
4819
{
4825
- Arg = arg ;
4820
+ Args = args ;
4826
4821
This = onInstance ;
4827
4822
Evaluator = evaluator ;
4828
4823
}
4829
4824
4830
4825
/// <summary>
4831
4826
/// The not evaluated args of the indexing
4832
4827
/// </summary>
4833
- public string Arg { get ; set ; }
4828
+ public List < string > Args { get ; } = new List < string > ( ) ;
4834
4829
4835
4830
/// <summary>
4836
4831
/// The instance of the object on which the indexing is called.
@@ -4866,18 +4861,30 @@ public object Value
4866
4861
/// Get the values of the indexing's args.
4867
4862
/// </summary>
4868
4863
/// <returns></returns>
4869
- public object EvaluateArg ( )
4864
+ public object [ ] EvaluateArgs ( )
4870
4865
{
4871
- return Evaluator . Evaluate ( Arg ) ;
4866
+ return Args . ConvertAll ( arg => Evaluator . Evaluate ( arg ) ) . ToArray ( ) ;
4872
4867
}
4873
4868
4874
4869
/// <summary>
4875
- /// Get the values of the indexing's args.
4870
+ /// Get the value of the indexing's arg at the specified index
4876
4871
/// </summary>
4877
- /// <returns></returns>
4878
- public T EvaluateArg < T > ( )
4872
+ /// <param name="index">The index of the indexing's arg to evaluate</param>
4873
+ /// <returns>The evaluated arg</returns>
4874
+ public object EvaluateArg ( int index )
4879
4875
{
4880
- return Evaluator . Evaluate < T > ( Arg ) ;
4876
+ return Evaluator . Evaluate ( Args [ index ] ) ;
4877
+ }
4878
+
4879
+ /// <summary>
4880
+ /// Get the value of the indexing's arg at the specified index
4881
+ /// </summary>
4882
+ /// <typeparam name="T">The type of the result to get. (Do a cast)</typeparam>
4883
+ /// <param name="index">The index of the indexing's arg to evaluate</param>
4884
+ /// <returns>The evaluated arg casted in the specified type</returns>
4885
+ public T EvaluateArg < T > ( int index )
4886
+ {
4887
+ return Evaluator . Evaluate < T > ( Args [ index ] ) ;
4881
4888
}
4882
4889
4883
4890
/// <summary>
0 commit comments