@@ -149,6 +149,7 @@ interface IEnumerableSignatures
149
149
void Take ( int count ) ;
150
150
void TakeWhile ( bool predicate ) ;
151
151
void Distinct ( ) ;
152
+ void GroupBy ( object selector ) ;
152
153
153
154
//Executors
154
155
void Single ( ) ;
@@ -222,6 +223,7 @@ interface IEnumerableSignatures
222
223
223
224
readonly Dictionary < string , object > _symbols ;
224
225
IDictionary < string , object > _externals ;
226
+ readonly Dictionary < string , object > _internals ;
225
227
readonly Dictionary < Expression , string > _literals ;
226
228
ParameterExpression _it ;
227
229
ParameterExpression _parent ;
@@ -269,6 +271,7 @@ public ExpressionParser(ParameterExpression[] parameters, string expression, obj
269
271
{
270
272
if ( _keywords == null ) _keywords = CreateKeywords ( ) ;
271
273
_symbols = new Dictionary < string , object > ( StringComparer . OrdinalIgnoreCase ) ;
274
+ _internals = new Dictionary < string , object > ( ) ;
272
275
_literals = new Dictionary < Expression , string > ( ) ;
273
276
if ( parameters != null ) ProcessParameters ( parameters ) ;
274
277
if ( values != null ) ProcessValues ( values ) ;
@@ -388,7 +391,7 @@ Expression ParseExpression()
388
391
// ?? (null-coalescing) operator
389
392
Expression ParseNullCoalescing ( )
390
393
{
391
- Expression expr = ParseConditionalOr ( ) ;
394
+ Expression expr = ParseLambda ( ) ;
392
395
if ( _textParser . CurrentToken . Id == TokenId . NullCoalescing )
393
396
{
394
397
_textParser . NextToken ( ) ;
@@ -398,6 +401,24 @@ Expression ParseNullCoalescing()
398
401
return expr ;
399
402
}
400
403
404
+ // => operator - Added Support for projection operator
405
+ Expression ParseLambda ( )
406
+ {
407
+ Expression expr = ParseConditionalOr ( ) ;
408
+ if ( _textParser . CurrentToken . Id == TokenId . Lambda && _it . Type == expr . Type )
409
+ {
410
+ _textParser . NextToken ( ) ;
411
+ if ( _textParser . CurrentToken . Id == TokenId . Identifier ||
412
+ _textParser . CurrentToken . Id == TokenId . OpenParen )
413
+ {
414
+ var right = ParseExpression ( ) ;
415
+ return Expression . Lambda ( right , new [ ] { ( ParameterExpression ) expr } ) ;
416
+ }
417
+ _textParser . ValidateToken ( TokenId . OpenParen , Res . OpenParenExpected ) ;
418
+ }
419
+ return expr ;
420
+ }
421
+
401
422
// isnull(a,b) operator
402
423
Expression ParseIsNull ( )
403
424
{
@@ -1003,7 +1024,8 @@ Expression ParseIdentifier()
1003
1024
}
1004
1025
1005
1026
if ( _symbols . TryGetValue ( _textParser . CurrentToken . Text , out value ) ||
1006
- _externals != null && _externals . TryGetValue ( _textParser . CurrentToken . Text , out value ) )
1027
+ _externals != null && _externals . TryGetValue ( _textParser . CurrentToken . Text , out value ) ||
1028
+ _internals . TryGetValue ( _textParser . CurrentToken . Text , out value ) )
1007
1029
{
1008
1030
Expression expr = value as Expression ;
1009
1031
if ( expr == null )
@@ -1093,7 +1115,24 @@ Expression GenerateConditional(Expression test, Expression expr1, Expression exp
1093
1115
Expression ParseNew ( )
1094
1116
{
1095
1117
_textParser . NextToken ( ) ;
1096
- _textParser . ValidateToken ( TokenId . OpenParen , Res . OpenParenExpected ) ;
1118
+ if ( _textParser . CurrentToken . Id != TokenId . OpenParen &&
1119
+ _textParser . CurrentToken . Id != TokenId . OpenCurlyParen &&
1120
+ _textParser . CurrentToken . Id != TokenId . Identifier )
1121
+ throw ParseError ( Res . OpenParenOrIdentifierExpected ) ;
1122
+
1123
+ Type newType = null ;
1124
+ if ( _textParser . CurrentToken . Id == TokenId . Identifier )
1125
+ {
1126
+ var newTypeName = _textParser . CurrentToken . Text ;
1127
+ newType = FindType ( newTypeName ) ;
1128
+ if ( newType == null )
1129
+ throw ParseError ( _textParser . CurrentToken . Pos , Res . TypeNotFound , newTypeName ) ;
1130
+ _textParser . NextToken ( ) ;
1131
+ if ( _textParser . CurrentToken . Id != TokenId . OpenParen &&
1132
+ _textParser . CurrentToken . Id != TokenId . OpenCurlyParen )
1133
+ throw ParseError ( Res . OpenParenExpected ) ;
1134
+ }
1135
+
1097
1136
_textParser . NextToken ( ) ;
1098
1137
1099
1138
var properties = new List < DynamicProperty > ( ) ;
@@ -1125,16 +1164,18 @@ Expression ParseNew()
1125
1164
_textParser . NextToken ( ) ;
1126
1165
}
1127
1166
1128
- _textParser . ValidateToken ( TokenId . CloseParen , Res . CloseParenOrCommaExpected ) ;
1167
+ if ( _textParser . CurrentToken . Id != TokenId . CloseParen &&
1168
+ _textParser . CurrentToken . Id != TokenId . CloseCurlyParen )
1169
+ throw ParseError ( Res . CloseParenOrCommaExpected ) ;
1129
1170
_textParser . NextToken ( ) ;
1130
1171
1131
- return CreateNewExpression ( properties , expressions ) ;
1172
+ return CreateNewExpression ( properties , expressions , newType ) ;
1132
1173
}
1133
1174
1134
- private Expression CreateNewExpression ( List < DynamicProperty > properties , List < Expression > expressions )
1175
+ private Expression CreateNewExpression ( List < DynamicProperty > properties , List < Expression > expressions , Type newType )
1135
1176
{
1136
1177
// http://solutionizing.net/category/linq/
1137
- Type type = _resultType ;
1178
+ Type type = newType ?? _resultType ;
1138
1179
1139
1180
if ( type == null )
1140
1181
{
@@ -1323,7 +1364,19 @@ Expression ParseMemberAccess(Type type, Expression instance)
1323
1364
MemberInfo member = FindPropertyOrField ( type , id , instance == null ) ;
1324
1365
if ( member == null )
1325
1366
{
1326
- throw ParseError ( errorPos , Res . UnknownPropertyOrField , id , GetTypeName ( type ) ) ;
1367
+ if ( _textParser . CurrentToken . Id == TokenId . Lambda && _it . Type == type )
1368
+ {
1369
+ // This might be an internal variable for use within a lambda expression, so store it as such
1370
+ _internals . Add ( id , _it ) ;
1371
+ _textParser . NextToken ( ) ;
1372
+ var right = ParseExpression ( ) ;
1373
+ return right ;
1374
+ }
1375
+ else
1376
+ {
1377
+ throw ParseError ( errorPos , Res . UnknownPropertyOrField , id , GetTypeName ( type ) ) ;
1378
+ }
1379
+
1327
1380
}
1328
1381
1329
1382
var property = member as PropertyInfo ;
@@ -1352,6 +1405,28 @@ static Type FindGenericType(Type generic, Type type)
1352
1405
return null ;
1353
1406
}
1354
1407
1408
+ Type FindType ( string name )
1409
+ {
1410
+ object type ;
1411
+ _keywords . TryGetValue ( name , out type ) ;
1412
+ var result = type as Type ;
1413
+ if ( result != null )
1414
+ return result ;
1415
+ if ( _it != null && _it . Type . Name == name )
1416
+ return _it . Type ;
1417
+ if ( _parent != null && _parent . Type . Name == name )
1418
+ return _parent . Type ;
1419
+ if ( _root != null && _root . Type . Name == name )
1420
+ return _root . Type ;
1421
+ if ( _it != null && _it . Type . Namespace + "." + _it . Type . Name == name )
1422
+ return _it . Type ;
1423
+ if ( _parent != null && _parent . Type . Namespace + "." + _parent . Type . Name == name )
1424
+ return _parent . Type ;
1425
+ if ( _root != null && _root . Type . Namespace + "." + _root . Type . Name == name )
1426
+ return _root . Type ;
1427
+ return null ;
1428
+ }
1429
+
1355
1430
Expression ParseAggregate ( Expression instance , Type elementType , string methodName , int errorPos )
1356
1431
{
1357
1432
var oldParent = _parent ;
@@ -1381,7 +1456,7 @@ Expression ParseAggregate(Expression instance, Type elementType, string methodNa
1381
1456
throw ParseError ( errorPos , Res . NoApplicableAggregate , methodName ) ;
1382
1457
1383
1458
Type [ ] typeArgs ;
1384
- if ( new [ ] { "Min" , "Max" , "Select" , "OrderBy" , "OrderByDescending" , "ThenBy" , "ThenByDescending" } . Contains ( signature . Name ) )
1459
+ if ( new [ ] { "Min" , "Max" , "Select" , "OrderBy" , "OrderByDescending" , "ThenBy" , "ThenByDescending" , "GroupBy" } . Contains ( signature . Name ) )
1385
1460
{
1386
1461
typeArgs = new [ ] { elementType , args [ 0 ] . Type } ;
1387
1462
}
@@ -2360,10 +2435,6 @@ static Expression OptimizeStringForEqualityIfPossible(string text, Type type)
2360
2435
return null ;
2361
2436
}
2362
2437
2363
-
2364
-
2365
-
2366
-
2367
2438
bool TokenIdentifierIs ( string id )
2368
2439
{
2369
2440
return _textParser . CurrentToken . Id == TokenId . Identifier && string . Equals ( id , _textParser . CurrentToken . Text , StringComparison . OrdinalIgnoreCase ) ;
0 commit comments