@@ -38,7 +38,7 @@ public partial class ExpressionEvaluator
3838 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 ) ;
3939 protected static readonly Regex stringBeginningRegex = new Regex ( "^(?<interpolated>[$])?(?<escaped>[@])?[\" ]" , RegexOptions . Compiled ) ;
4040 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 ) ;
4242 protected static readonly Regex arrayTypeDetectionRegex = new Regex ( @"^(\s*(\[(?>(?>\s+)|[,])*)\])+" , RegexOptions . Compiled ) ;
4343 protected static readonly Regex assignationOrPostFixOperatorRegex = new Regex ( @"^(?>\s*)((?<assignmentPrefix>[+\-*/%&|^]|<<|>>|\?\?)?=(?![=>])|(?<postfixOperator>([+][+]|--)(?![\p{L}_0-9])))" ) ;
4444 protected static readonly Regex genericsDecodeRegex = new Regex ( "(?<name>[^,<>]+)(?<isgeneric>[<](?>[^<>]+|(?<gentag>[<])|(?<-gentag>[>]))*(?(gentag)(?!))[>])?" , RegexOptions . Compiled ) ;
@@ -229,45 +229,9 @@ protected enum TryBlockEvaluatedState
229229 protected virtual IList < ExpressionOperator > RightOperandOnlyOperatorsEvaluationDictionary => rightOperandOnlyOperatorsEvaluationDictionary ;
230230 protected virtual IList < IDictionary < ExpressionOperator , Func < dynamic , dynamic , object > > > OperatorsEvaluations => operatorsEvaluations ;
231231
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-
256232 protected static readonly IList < IDictionary < ExpressionOperator , Func < dynamic , dynamic , object > > > operatorsEvaluations =
257233 new List < IDictionary < ExpressionOperator , Func < dynamic , dynamic , object > > > ( )
258234 {
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- } ,
271235 new Dictionary < ExpressionOperator , Func < dynamic , dynamic , object > > ( )
272236 {
273237 { ExpressionOperator . UnaryPlus , ( dynamic _ , dynamic right ) => + right } ,
@@ -2779,54 +2743,24 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
27792743
27802744 if ( indexingBeginningMatch . Success )
27812745 {
2782- StringBuilder innerExp = new StringBuilder ( ) ;
27832746 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- }
28152747
28162748 dynamic left = stack . Pop ( ) ;
28172749
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 )
28192753 {
28202754 stack . Push ( left ) ;
28212755 return true ;
28222756 }
2823- else if ( left is BubbleExceptionContainer )
2757+ else if ( indexingBeginningMatch . Groups [ "nullConditional" ] . Success && left == null )
28242758 {
2825- stack . Push ( left ) ;
2759+ stack . Push ( new NullConditionalNullValue ( ) ) ;
28262760 return true ;
28272761 }
28282762
2829- IndexingPreEvaluationEventArg indexingPreEvaluationEventArg = new IndexingPreEvaluationEventArg ( innerExp . ToString ( ) , this , left ) ;
2763+ IndexingPreEvaluationEventArg indexingPreEvaluationEventArg = new IndexingPreEvaluationEventArg ( indexingArgs , this , left ) ;
28302764
28312765 PreEvaluateIndexing ? . Invoke ( this , indexingPreEvaluationEventArg ) ;
28322766
@@ -2840,15 +2774,67 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
28402774 }
28412775 else
28422776 {
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 ;
28452781
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 ( ) ;
28482785
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 ( ) ;
28502797
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+ }
28522838
28532839 if ( OptionIndexingAssignationActive && ( assignationOrPostFixOperatorMatch = assignationOrPostFixOperatorRegex . Match ( expression . Substring ( i + 1 ) ) ) . Success )
28542840 {
@@ -2860,31 +2846,42 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
28602846 if ( stack . Count > 1 )
28612847 throw new ExpressionEvaluatorSyntaxErrorException ( $ "The left part of { exceptionContext } must be a variable, a property or an indexer.") ;
28622848
2863- if ( op == ExpressionOperator . IndexingWithNullConditional )
2849+ if ( indexingBeginningMatch . Groups [ "nullConditional" ] . Success )
28642850 throw new ExpressionEvaluatorSyntaxErrorException ( $ "Null conditional is not usable left to { exceptionContext } ") ;
28652851
28662852 if ( postFixOperator )
28672853 {
28682854 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+ }
28702863 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+ }
28722867 }
28732868 else
28742869 {
2875- valueToPush = ManageKindOfAssignation ( expression , ref i , assignationOrPostFixOperatorMatch , ( ) => OperatorsEvaluations [ 0 ] [ op ] ( left , right ) ) ;
2870+ valueToPush = ManageKindOfAssignation ( expression , ref i , assignationOrPostFixOperatorMatch , GetIndexedObject ) ;
28762871
28772872 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 ( ) ) ;
28792876 else
2880- left [ right ] = valueToPush ;
2877+ left [ oIndexingArgs [ 0 ] ] = valueToPush ;
28812878
28822879 stack . Clear ( ) ;
28832880 }
28842881 }
28852882 else
28862883 {
2887- valueToPush = OperatorsEvaluations [ 0 ] [ op ] ( left , right ) ;
2884+ valueToPush = GetIndexedObject ( ) ;
28882885 }
28892886
28902887 stack . Push ( valueToPush ) ;
@@ -4391,8 +4388,6 @@ protected ExpressionOperator()
43914388 public static readonly ExpressionOperator ShiftBitsRight = new ExpressionOperator ( ) ;
43924389 public static readonly ExpressionOperator NullCoalescing = new ExpressionOperator ( ) ;
43934390 public static readonly ExpressionOperator Cast = new ExpressionOperator ( ) ;
4394- public static readonly ExpressionOperator Indexing = new ExpressionOperator ( ) ;
4395- public static readonly ExpressionOperator IndexingWithNullConditional = new ExpressionOperator ( ) ;
43964391
43974392 public override bool Equals ( object obj )
43984393 {
@@ -4820,17 +4815,17 @@ public FunctionPreEvaluationEventArg(string name, List<string> args = null, Expr
48204815 /// </summary>
48214816 public partial class IndexingPreEvaluationEventArg : EventArgs
48224817 {
4823- public IndexingPreEvaluationEventArg ( string arg , ExpressionEvaluator evaluator , object onInstance )
4818+ public IndexingPreEvaluationEventArg ( List < string > args , ExpressionEvaluator evaluator , object onInstance )
48244819 {
4825- Arg = arg ;
4820+ Args = args ;
48264821 This = onInstance ;
48274822 Evaluator = evaluator ;
48284823 }
48294824
48304825 /// <summary>
48314826 /// The not evaluated args of the indexing
48324827 /// </summary>
4833- public string Arg { get ; set ; }
4828+ public List < string > Args { get ; } = new List < string > ( ) ;
48344829
48354830 /// <summary>
48364831 /// The instance of the object on which the indexing is called.
@@ -4866,18 +4861,30 @@ public object Value
48664861 /// Get the values of the indexing's args.
48674862 /// </summary>
48684863 /// <returns></returns>
4869- public object EvaluateArg ( )
4864+ public object [ ] EvaluateArgs ( )
48704865 {
4871- return Evaluator . Evaluate ( Arg ) ;
4866+ return Args . ConvertAll ( arg => Evaluator . Evaluate ( arg ) ) . ToArray ( ) ;
48724867 }
48734868
48744869 /// <summary>
4875- /// Get the values of the indexing's args.
4870+ /// Get the value of the indexing's arg at the specified index
48764871 /// </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 )
48794875 {
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 ] ) ;
48814888 }
48824889
48834890 /// <summary>
0 commit comments