Skip to content

Commit 4c16307

Browse files
committed
Work on zzzprojects#66 -> Should work now. Tests will follow on VS2017 support!
1 parent b1a05ac commit 4c16307

File tree

4 files changed

+82
-8
lines changed

4 files changed

+82
-8
lines changed

src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs

+18
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@ namespace System.Linq.Dynamic.Core
99
/// </summary>
1010
public static class DynamicExpressionParser
1111
{
12+
/// <summary>
13+
/// Parses a expression into a LambdaExpression. (Also create a constructor for all the parameters.)
14+
/// </summary>
15+
/// <param name="itType">The main type from the dynamic class expression.</param>
16+
/// <param name="resultType">Type of the result. If not specified, it will be generated dynamically.</param>
17+
/// <param name="expression">The expression.</param>
18+
/// <param name="values">An object array that contains zero or more objects which are used as replacement values.</param>
19+
/// <returns>The generated <see cref="LambdaExpression"/></returns>
20+
public static LambdaExpression ParseLambda([CanBeNull] Type resultType, [NotNull] string expression, params object[] values)
21+
{
22+
Check.NotEmpty(expression, nameof(expression));
23+
24+
25+
var parser = new ExpressionParser(new ParameterExpression[0], expression, values);
26+
27+
return Expression.Lambda(parser.Parse(resultType, true));
28+
}
29+
1230
/// <summary>
1331
/// Parses a expression into a LambdaExpression. (Also create a constructor for all the parameters.)
1432
/// </summary>

src/System.Linq.Dynamic.Core/ExpressionParser.cs

+45-7
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ interface IEnumerableSignatures
149149
void Take(int count);
150150
void TakeWhile(bool predicate);
151151
void Distinct();
152+
void GroupBy(object selector);
152153

153154
//Executors
154155
void Single();
@@ -222,6 +223,7 @@ interface IEnumerableSignatures
222223

223224
readonly Dictionary<string, object> _symbols;
224225
IDictionary<string, object> _externals;
226+
readonly Dictionary<string, object> _internals;
225227
readonly Dictionary<Expression, string> _literals;
226228
ParameterExpression _it;
227229
ParameterExpression _parent;
@@ -269,6 +271,7 @@ public ExpressionParser(ParameterExpression[] parameters, string expression, obj
269271
{
270272
if (_keywords == null) _keywords = CreateKeywords();
271273
_symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
274+
_internals = new Dictionary<string, object>();
272275
_literals = new Dictionary<Expression, string>();
273276
if (parameters != null) ProcessParameters(parameters);
274277
if (values != null) ProcessValues(values);
@@ -388,7 +391,7 @@ Expression ParseExpression()
388391
// ?? (null-coalescing) operator
389392
Expression ParseNullCoalescing()
390393
{
391-
Expression expr = ParseConditionalOr();
394+
Expression expr = ParseLambda();
392395
if (_textParser.CurrentToken.Id == TokenId.NullCoalescing)
393396
{
394397
_textParser.NextToken();
@@ -398,6 +401,24 @@ Expression ParseNullCoalescing()
398401
return expr;
399402
}
400403

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+
401422
// isnull(a,b) operator
402423
Expression ParseIsNull()
403424
{
@@ -1003,7 +1024,8 @@ Expression ParseIdentifier()
10031024
}
10041025

10051026
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))
10071029
{
10081030
Expression expr = value as Expression;
10091031
if (expr == null)
@@ -1093,7 +1115,9 @@ Expression GenerateConditional(Expression test, Expression expr1, Expression exp
10931115
Expression ParseNew()
10941116
{
10951117
_textParser.NextToken();
1096-
_textParser.ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
1118+
if (_textParser.CurrentToken.Id != TokenId.OpenParen &&
1119+
_textParser.CurrentToken.Id != TokenId.OpenCurlyParen)
1120+
throw ParseError(Res.OpenParenExpected);
10971121
_textParser.NextToken();
10981122

10991123
var properties = new List<DynamicProperty>();
@@ -1125,7 +1149,9 @@ Expression ParseNew()
11251149
_textParser.NextToken();
11261150
}
11271151

1128-
_textParser.ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
1152+
if (_textParser.CurrentToken.Id != TokenId.CloseParen &&
1153+
_textParser.CurrentToken.Id != TokenId.CloseCurlyParen)
1154+
throw ParseError(Res.CloseParenOrCommaExpected);
11291155
_textParser.NextToken();
11301156

11311157
return CreateNewExpression(properties, expressions);
@@ -1134,7 +1160,7 @@ Expression ParseNew()
11341160
private Expression CreateNewExpression(List<DynamicProperty> properties, List<Expression> expressions)
11351161
{
11361162
// http://solutionizing.net/category/linq/
1137-
Type type = _resultType;
1163+
Type type = null; //_resultType;
11381164

11391165
if (type == null)
11401166
{
@@ -1323,7 +1349,19 @@ Expression ParseMemberAccess(Type type, Expression instance)
13231349
MemberInfo member = FindPropertyOrField(type, id, instance == null);
13241350
if (member == null)
13251351
{
1326-
throw ParseError(errorPos, Res.UnknownPropertyOrField, id, GetTypeName(type));
1352+
if (_textParser.CurrentToken.Id == TokenId.Lambda && _it.Type == type)
1353+
{
1354+
// This might be an internal variable for use within a lambda expression, so store it as such
1355+
_internals.Add(id, _it);
1356+
_textParser.NextToken();
1357+
var right = ParseExpression();
1358+
return right;
1359+
}
1360+
else
1361+
{
1362+
throw ParseError(errorPos, Res.UnknownPropertyOrField, id, GetTypeName(type));
1363+
}
1364+
13271365
}
13281366

13291367
var property = member as PropertyInfo;
@@ -1381,7 +1419,7 @@ Expression ParseAggregate(Expression instance, Type elementType, string methodNa
13811419
throw ParseError(errorPos, Res.NoApplicableAggregate, methodName);
13821420

13831421
Type[] typeArgs;
1384-
if (new[] { "Min", "Max", "Select", "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending" }.Contains(signature.Name))
1422+
if (new[] { "Min", "Max", "Select", "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending", "GroupBy" }.Contains(signature.Name))
13851423
{
13861424
typeArgs = new[] { elementType, args[0].Type };
13871425
}

src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs

+15
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ public void NextToken()
100100
t = TokenId.CloseParen;
101101
break;
102102

103+
case '{':
104+
NextChar();
105+
t = TokenId.OpenCurlyParen;
106+
break;
107+
108+
case '}':
109+
NextChar();
110+
t = TokenId.CloseCurlyParen;
111+
break;
112+
103113
case '*':
104114
NextChar();
105115
t = TokenId.Asterisk;
@@ -164,6 +174,11 @@ public void NextToken()
164174
NextChar();
165175
t = TokenId.DoubleEqual;
166176
}
177+
else if (_ch == '>')
178+
{
179+
NextChar();
180+
t = TokenId.Lambda;
181+
}
167182
else
168183
{
169184
t = TokenId.Equal;

src/System.Linq.Dynamic.Core/Tokenizer/TokenId.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ internal enum TokenId
1313
Amphersand,
1414
OpenParen,
1515
CloseParen,
16+
OpenCurlyParen,
17+
CloseCurlyParen,
1618
Asterisk,
1719
Plus,
1820
Comma,
@@ -36,6 +38,7 @@ internal enum TokenId
3638
DoubleBar,
3739
DoubleGreaterThan,
3840
DoubleLessThan,
39-
NullCoalescing
41+
NullCoalescing,
42+
Lambda
4043
}
4144
}

0 commit comments

Comments
 (0)