Skip to content

Commit 28e7cd9

Browse files
author
Sébastien Geiser
committed
Generics on the fly evaluation OK + type evaluation bug correction
1 parent 09102f2 commit 28e7cd9

File tree

2 files changed

+118
-29
lines changed

2 files changed

+118
-29
lines changed

CodingSeb.ExpressionEvaluator.Tests/ExpressionEvaluatorTests.cs

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public void TypeTesting(string expression, Type type)
125125
[TestCase("\"\\n\"", TestOf = typeof(string), ExpectedResult = "\n", Category = "StringEscape")]
126126
[TestCase("\"\\r\"", TestOf = typeof(string), ExpectedResult = "\r", Category = "StringEscape")]
127127
[TestCase("\"\\t\"", TestOf = typeof(string), ExpectedResult = "\t", Category = "StringEscape")]
128-
[TestCase("\""+ @"\\" + "\"", TestOf = typeof(string), ExpectedResult = @"\", Category = "StringEscape")]
128+
[TestCase("\"" + @"\\" + "\"", TestOf = typeof(string), ExpectedResult = @"\", Category = "StringEscape")]
129129
[TestCase("\"" + @"\\\n" + "\"", TestOf = typeof(string), ExpectedResult = "\\\n", Category = "StringEscape")]
130130
[TestCase("@\"" + @"\\n" + "\"", TestOf = typeof(string), ExpectedResult = @"\\n", Category = "StringEscape")]
131131

@@ -135,7 +135,7 @@ public void TypeTesting(string expression, Type type)
135135
[TestCase("$\"{{\"", TestOf = typeof(string), ExpectedResult = "{", Category = "StringInterpolation")]
136136
[TestCase("$\"{ \"{\" }\"", TestOf = typeof(string), ExpectedResult = "{", Category = "StringInterpolation")]
137137
[TestCase("$\"Test { 5+5 } Test\"", TestOf = typeof(string), ExpectedResult = "Test 10 Test", Category = "StringInterpolation")]
138-
[TestCase("$\"Test { 5+5 + \" Test\" } Test\"", TestOf = typeof(string), ExpectedResult = "Test 10 Test Test", Category = "StringInterpolation")]
138+
[TestCase("$\"Test { 5+5 + \" Test\" } Test\"", TestOf = typeof(string), ExpectedResult = "Test 10 Test Test", Category = "StringInterpolation")]
139139
[TestCase("$\"Test { 5+5 + \" Test{\" } Test\"", TestOf = typeof(string), ExpectedResult = "Test 10 Test{ Test", Category = "StringInterpolation")]
140140
[TestCase("$\"Test { 5+5 + \" Test{{ }\" } Test\"", TestOf = typeof(string), ExpectedResult = "Test 10 Test{{ } Test", Category = "StringInterpolation")]
141141

@@ -505,7 +505,7 @@ public void TypeTesting(string expression, Type type)
505505
[TestCase("default(bool)", TestOf = typeof(bool), ExpectedResult = false, Category = "default values")]
506506
[TestCase("default(System.Boolean)", TestOf = typeof(bool), ExpectedResult = false, Category = "default values, Inline namespaces")]
507507
#endregion
508-
508+
509509
#region typeof keyword
510510
[TestCase("typeof(int)", ExpectedResult = typeof(int), Category = "typeof keyword")]
511511
[TestCase("typeof(float)", ExpectedResult = typeof(float), Category = "typeof keyword")]
@@ -953,9 +953,9 @@ public void TypeTesting(string expression, Type type)
953953

954954
#region Generic types Management
955955

956-
[TestCase("List(\"Hello\", \"Test\").Cast<string>().ToList<string>().GetType()", ExpectedResult = typeof(List<string>) , Category = "List function, Generics")]
957-
[TestCase("new List<string>().GetType()", ExpectedResult = typeof(List<string>) , Category = "new Keyword, Generics")]
958-
[TestCase("new Dictionary<string,List<int>>().GetType()", ExpectedResult = typeof(Dictionary<string, List<int>>) , Category = "new Keyword, Generics")]
956+
[TestCase("List(\"Hello\", \"Test\").Cast<string>().ToList<string>().GetType()", ExpectedResult = typeof(List<string>), Category = "List function, Generics")]
957+
[TestCase("new List<string>().GetType()", ExpectedResult = typeof(List<string>), Category = "new Keyword, Generics")]
958+
[TestCase("new Dictionary<string,List<int>>().GetType()", ExpectedResult = typeof(Dictionary<string, List<int>>), Category = "new Keyword, Generics")]
959959

960960
#endregion
961961

@@ -1136,7 +1136,7 @@ public static IEnumerable<TestCaseData> TestCasesForWithCustomVariablesExpressio
11361136
#endregion
11371137

11381138
#region Delegates as Property of object
1139-
1139+
11401140
yield return new TestCaseData("customObject.AddAsDelegate(6, 10)", onInstanceVariables, true).SetCategory("Delegate as a instance Property").Returns(16);
11411141
yield return new TestCaseData("ClassForTest1.AddAsStaticDelegate(6, 10)", onInstanceVariables, true).SetCategory("Delegate as a static Property").Returns(16);
11421142

@@ -1167,7 +1167,7 @@ public object WithCustomVariablesExpressionEvaluation(string expression, Diction
11671167
[TestCase("3.Add(2)", ExpectedResult = 5, Category = "On the fly method")]
11681168
[TestCase("3.MultipliedBy2", ExpectedResult = 6, Category = "On the fly property")]
11691169
[TestCase("myVar + 2", ExpectedResult = 10, Category = "On the fly variable")]
1170-
[TestCase("SayHello(\"Bob\")", ExpectedResult = "Hello Bob", Category = "On the fly variable")]
1170+
[TestCase("SayHello(\"Bob\")", ExpectedResult = "Hello Bob", Category = "On the fly func")]
11711171
#endregion
11721172
public object OnTheFlyEvaluation(string expression)
11731173
{
@@ -1192,6 +1192,10 @@ private void Evaluator_EvaluateFunction(object sender, FunctionEvaluationEventAr
11921192
{
11931193
e.Value = $"Hello {e.EvaluateArg(0)}";
11941194
}
1195+
else if (e.Name.Equals("GetSpecifiedGenericTypesFunc"))
1196+
{
1197+
e.Value = e.EvaluateGenericTypes();
1198+
}
11951199
}
11961200

11971201
private void Evaluator_EvaluateVariable(object sender, VariableEvaluationEventArg e)
@@ -1208,6 +1212,10 @@ private void Evaluator_EvaluateVariable(object sender, VariableEvaluationEventAr
12081212
{
12091213
e.Value = JsonConvert.SerializeObject(e.This);
12101214
}
1215+
else if (e.Name.Equals("GetSpecifiedGenericTypesProp"))
1216+
{
1217+
e.Value = e.EvaluateGenericTypes();
1218+
}
12111219
}
12121220

12131221
#endregion
@@ -1462,16 +1470,16 @@ public static IEnumerable<TestCaseData> TestCasesEvaluateWithSpecificEvaluator
14621470
.SetCategory("Options")
14631471
.SetCategory("Numbers Culture");
14641472

1465-
yield return new TestCaseData(new ExpressionEvaluator
1473+
yield return new TestCaseData(new ExpressionEvaluator
14661474
{
14671475
OptionNumberParsingDecimalSeparator = ",",
14681476
OptionNumberParsingThousandSeparator = "'",
14691477
OptionInitializersSeparator = ";"
14701478
}
1471-
, "new double[]{1'200,5; 1'111'000,7}.Max()")
1472-
.Returns(1111000.7)
1473-
.SetCategory("Options")
1474-
.SetCategory("Numbers Culture");
1479+
, "new double[]{1'200,5; 1'111'000,7}.Max()")
1480+
.Returns(1111000.7)
1481+
.SetCategory("Options")
1482+
.SetCategory("Numbers Culture");
14751483

14761484
#endregion
14771485

@@ -1485,9 +1493,9 @@ public static IEnumerable<TestCaseData> TestCasesEvaluateWithSpecificEvaluator
14851493

14861494

14871495
yield return new TestCaseData(new ExpressionEvaluator
1488-
{
1489-
OptionForceIntegerNumbersEvaluationsAsDoubleByDefault = false
1490-
}
1496+
{
1497+
OptionForceIntegerNumbersEvaluationsAsDoubleByDefault = false
1498+
}
14911499
, "(130-120)/(2*250)")
14921500
.Returns(0)
14931501
.SetCategory("Options")
@@ -1510,7 +1518,7 @@ public static IEnumerable<TestCaseData> TestCasesEvaluateWithSpecificEvaluator
15101518
.SetCategory("Bug")
15111519
.SetCategory("Options")
15121520
.SetCategory("Integer Numbers default types");
1513-
1521+
15141522
yield return new TestCaseData(evaluatorWithIntForceToDouble
15151523
, "Round(5.54,1, MidpointRounding.ToEven)")
15161524
.Returns(5.5)
@@ -1533,6 +1541,49 @@ public static IEnumerable<TestCaseData> TestCasesEvaluateWithSpecificEvaluator
15331541
.SetCategory("Integer Numbers default types");
15341542

15351543
#endregion
1544+
1545+
#region GenericTypes in onthefly events
1546+
1547+
ExpressionEvaluator evaluatorOnTheFlyGenericTypes = new ExpressionEvaluator();
1548+
1549+
void VariableEval(object sender, VariableEvaluationEventArg e)
1550+
{
1551+
e.Value = e.EvaluateGenericTypes();
1552+
}
1553+
1554+
void FunctionEval(object sender, FunctionEvaluationEventArg e)
1555+
{
1556+
e.Value = e.EvaluateGenericTypes();
1557+
}
1558+
1559+
evaluatorOnTheFlyGenericTypes.EvaluateVariable += VariableEval;
1560+
evaluatorOnTheFlyGenericTypes.EvaluateFunction += FunctionEval;
1561+
1562+
yield return new TestCaseData(evaluatorOnTheFlyGenericTypes
1563+
, "GetSpecifiedGenericTypesFunc<string>()[0]")
1564+
.Returns(typeof(string))
1565+
.SetCategory("On the fly func")
1566+
.SetCategory("GenericTypes");
1567+
1568+
yield return new TestCaseData(evaluatorOnTheFlyGenericTypes
1569+
, "GetSpecifiedGenericTypesVar<string>[0]")
1570+
.Returns(typeof(string))
1571+
.SetCategory("On the fly var")
1572+
.SetCategory("GenericTypes");
1573+
1574+
yield return new TestCaseData(evaluatorOnTheFlyGenericTypes
1575+
, "GetSpecifiedGenericTypesFunc<string, List<int>>()[1]")
1576+
.Returns(typeof(List<int>))
1577+
.SetCategory("On the fly func")
1578+
.SetCategory("GenericTypes");
1579+
1580+
yield return new TestCaseData(evaluatorOnTheFlyGenericTypes
1581+
, "GetSpecifiedGenericTypesFunc<string, List<int>>[1]")
1582+
.Returns(typeof(List<int>))
1583+
.SetCategory("On the fly var")
1584+
.SetCategory("GenericTypes");
1585+
1586+
#endregion
15361587
}
15371588
}
15381589

CodingSeb.ExpressionEvaluator/ExpressionEvaluator.cs

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,7 +1741,7 @@ private bool EvaluateVarOrFunc(string expr, string restOfExpression, Stack<objec
17411741
}
17421742
else
17431743
{
1744-
FunctionEvaluationEventArg functionEvaluationEventArg = new FunctionEvaluationEventArg(varFuncName, Evaluate, funcArgs, this, obj, string.IsNullOrEmpty(genericsTypes) ? null : GetConcreteTypes(genericsTypes).ToList());
1744+
FunctionEvaluationEventArg functionEvaluationEventArg = new FunctionEvaluationEventArg(varFuncName, Evaluate, funcArgs, this, obj, genericsTypes, GetConcreteTypes);
17451745

17461746
EvaluateFunction?.Invoke(this, functionEvaluationEventArg);
17471747

@@ -1835,7 +1835,7 @@ private bool EvaluateVarOrFunc(string expr, string restOfExpression, Stack<objec
18351835
}
18361836
else
18371837
{
1838-
FunctionEvaluationEventArg functionEvaluationEventArg = new FunctionEvaluationEventArg(varFuncName, Evaluate, funcArgs, this, genericTypes: string.IsNullOrEmpty(genericsTypes) ? null : GetConcreteTypes(genericsTypes).ToList());
1838+
FunctionEvaluationEventArg functionEvaluationEventArg = new FunctionEvaluationEventArg(varFuncName, Evaluate, funcArgs, this, genericTypes: genericsTypes, evaluateGenericTypes: GetConcreteTypes);
18391839

18401840
EvaluateFunction?.Invoke(this, functionEvaluationEventArg);
18411841

@@ -1946,7 +1946,7 @@ private bool EvaluateVarOrFunc(string expr, string restOfExpression, Stack<objec
19461946
}
19471947
else
19481948
{
1949-
VariableEvaluationEventArg variableEvaluationEventArg = new VariableEvaluationEventArg(varFuncName, this, obj, string.IsNullOrEmpty(genericsTypes) ? null : GetConcreteTypes(genericsTypes).ToList());
1949+
VariableEvaluationEventArg variableEvaluationEventArg = new VariableEvaluationEventArg(varFuncName, this, obj, genericsTypes, GetConcreteTypes);
19501950

19511951
EvaluateVariable?.Invoke(this, variableEvaluationEventArg);
19521952

@@ -2086,7 +2086,7 @@ private bool EvaluateVarOrFunc(string expr, string restOfExpression, Stack<objec
20862086
}
20872087
else
20882088
{
2089-
VariableEvaluationEventArg variableEvaluationEventArg = new VariableEvaluationEventArg(varFuncName, this, genericTypes: string.IsNullOrEmpty(genericsTypes) ? null : GetConcreteTypes(genericsTypes).ToList());
2089+
VariableEvaluationEventArg variableEvaluationEventArg = new VariableEvaluationEventArg(varFuncName, this, genericTypes: genericsTypes, evaluateGenericTypes: GetConcreteTypes);
20902090

20912091
EvaluateVariable?.Invoke(this, variableEvaluationEventArg);
20922092

@@ -2971,6 +2971,7 @@ private Type GetTypeByFriendlyName(string typeName, string genericTypes = "", bo
29712971
Type result = null;
29722972
try
29732973
{
2974+
typeName = typeName.Trim();
29742975
string formatedGenericTypes = string.Empty;
29752976

29762977
if (!genericTypes.Equals(string.Empty))
@@ -3279,16 +3280,20 @@ public ExpressionEvaluatorSecurityException(string message, Exception innerExcep
32793280

32803281
public class VariableEvaluationEventArg : EventArgs
32813282
{
3283+
private readonly Func<string, Type[]> evaluateGenericTypes = null;
3284+
private readonly string genericTypes = null;
3285+
32823286
/// <summary>
32833287
/// Constructor of the VariableEvaluationEventArg
32843288
/// </summary>
32853289
/// <param name="name">The name of the variable to Evaluate</param>
3286-
public VariableEvaluationEventArg(string name, ExpressionEvaluator evaluator = null, object onInstance = null, List<Type> genericTypes = null)
3290+
public VariableEvaluationEventArg(string name, ExpressionEvaluator evaluator = null, object onInstance = null, string genericTypes = null, Func<string, Type[]> evaluateGenericTypes = null)
32873291
{
32883292
Name = name;
32893293
This = onInstance;
32903294
Evaluator = evaluator;
3291-
GenericTypes = genericTypes ?? new List<Type>();
3295+
this.genericTypes = genericTypes;
3296+
this.evaluateGenericTypes = evaluateGenericTypes;
32923297
}
32933298

32943299
/// <summary>
@@ -3327,25 +3332,43 @@ public object Value
33273332
/// </summary>
33283333
public ExpressionEvaluator Evaluator { get; }
33293334

3335+
/// <summary>
3336+
/// Is <c>true</c> if some generic types are specified with &lt;&gt;.
3337+
/// <c>false</c> otherwise
3338+
/// </summary>
3339+
public bool HasGenericTypes
3340+
{
3341+
get
3342+
{
3343+
return string.IsNullOrEmpty(genericTypes);
3344+
}
3345+
}
3346+
33303347
/// <summary>
33313348
/// In the case where generic types are specified with &lt;&gt;
3332-
/// This list contains all specified Types.
3349+
/// Evaluate all types and return an array of types
33333350
/// </summary>
3334-
public List<Type> GenericTypes { get; }
3351+
public Type[] EvaluateGenericTypes()
3352+
{
3353+
return evaluateGenericTypes?.Invoke(genericTypes) ?? new Type[0];
3354+
}
33353355
}
33363356

33373357
public class FunctionEvaluationEventArg : EventArgs
33383358
{
33393359
private readonly Func<string, object> evaluateFunc = null;
3360+
private readonly Func<string, Type[]> evaluateGenericTypes = null;
3361+
private readonly string genericTypes = null;
33403362

3341-
public FunctionEvaluationEventArg(string name, Func<string, object> evaluateFunc, List<string> args = null, ExpressionEvaluator evaluator = null, object onInstance = null, List<Type> genericTypes = null)
3363+
public FunctionEvaluationEventArg(string name, Func<string, object> evaluateFunc, List<string> args = null, ExpressionEvaluator evaluator = null, object onInstance = null, string genericTypes = null, Func<string, Type[]> evaluateGenericTypes = null)
33423364
{
33433365
Name = name;
33443366
Args = args ?? new List<string>();
33453367
this.evaluateFunc = evaluateFunc;
33463368
This = onInstance;
33473369
Evaluator = evaluator;
3348-
GenericTypes = genericTypes ?? new List<Type>();
3370+
this.genericTypes = genericTypes;
3371+
this.evaluateGenericTypes = evaluateGenericTypes;
33493372
}
33503373

33513374
/// <summary>
@@ -3419,11 +3442,26 @@ public object Value
34193442
/// </summary>
34203443
public ExpressionEvaluator Evaluator { get; }
34213444

3445+
/// <summary>
3446+
/// Is <c>true</c> if some generic types are specified with &lt;&gt;.
3447+
/// <c>false</c> otherwise
3448+
/// </summary>
3449+
public bool HasGenericTypes
3450+
{
3451+
get
3452+
{
3453+
return string.IsNullOrEmpty(genericTypes);
3454+
}
3455+
}
3456+
34223457
/// <summary>
34233458
/// In the case where generic types are specified with &lt;&gt;
3424-
/// This list contains all specified Types.
3459+
/// Evaluate all types and return an array of types
34253460
/// </summary>
3426-
public List<Type> GenericTypes { get; }
3461+
public Type[] EvaluateGenericTypes()
3462+
{
3463+
return evaluateGenericTypes?.Invoke(genericTypes) ?? new Type[0];
3464+
}
34273465
}
34283466

34293467
#endregion

0 commit comments

Comments
 (0)