Skip to content

Commit c2f7d2c

Browse files
authored
Merge pull request #52 from codingseb/dev
Dev
2 parents 6cda5e0 + f4110a5 commit c2f7d2c

File tree

3 files changed

+72
-27
lines changed

3 files changed

+72
-27
lines changed

CodingSeb.ExpressionEvaluator.Tests/ExpressionEvaluatorTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ public void TypeTesting(string expression, Type type)
500500
[TestCase("null ?? \"Option2\"", TestOf = typeof(string), ExpectedResult = "Option2", Category = "Null Coalescing Operator")]
501501
#endregion
502502

503+
#region Null conditional Operator
504+
[TestCase("null?.Trim()", ExpectedResult = null, Category = "Null conditional Operator")]
505+
#endregion
506+
503507
#region default values
504508
[TestCase("default(int)", TestOf = typeof(int), ExpectedResult = 0, Category = "default values")]
505509
[TestCase("default(bool)", TestOf = typeof(bool), ExpectedResult = false, Category = "default values")]
@@ -1075,9 +1079,11 @@ public static IEnumerable<TestCaseData> TestCasesForWithCustomVariablesExpressio
10751079
Dictionary<string, object> onInstanceVariables = new Dictionary<string, object>()
10761080
{
10771081
{ "simpleArray", new object[] {2 , "Hello", true} },
1082+
{ "simpleArray2", new object[] {2 , " Hello ", true, null } },
10781083
{ "otherArray", new object[] {2 , "Hello", true, new ClassForTest1() { IntProperty = 18 } } },
10791084
{ "simpleList", new List<object>() {"Test" ,false, -15, 123.8f} },
10801085
{ "nullVar", null },
1086+
{ "notTrimmedString", " Hello " },
10811087
{ "simpleInt", 42 },
10821088
{ "simpleChar", 'n' },
10831089
{ "simpleLineFeed", '\n' },
@@ -1090,9 +1096,18 @@ public static IEnumerable<TestCaseData> TestCasesForWithCustomVariablesExpressio
10901096
yield return new TestCaseData("simpleList?.Count", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Property").Returns(4);
10911097
yield return new TestCaseData("nullVar?.Length", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Property").Returns(null);
10921098
yield return new TestCaseData("nullVar?.Count", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Property").Returns(null);
1099+
yield return new TestCaseData("nullVar?.Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Method").Returns(null);
1100+
yield return new TestCaseData("nullVar?.Trim().Length", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Method").Returns(null);
1101+
yield return new TestCaseData("notTrimmedString?.Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Method").Returns("Hello");
1102+
yield return new TestCaseData("notTrimmedString?.Trim().Length", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Method").Returns(5);
10931103
yield return new TestCaseData("simpleArray?[2]", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(true);
10941104
yield return new TestCaseData("simpleList?[2]", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(-15);
1105+
yield return new TestCaseData("simpleArray2[1].Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns("Hello");
10951106
yield return new TestCaseData("nullVar?[2]", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
1107+
yield return new TestCaseData("nullVar?[1].Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
1108+
yield return new TestCaseData("nullVar?[1]?.Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
1109+
yield return new TestCaseData("nullVar?[1][3]", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
1110+
yield return new TestCaseData("simpleArray2?[3]?.Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
10961111

10971112
yield return new TestCaseData("simpleInt.ToString()", onInstanceVariables, true).SetCategory("Instance Method").Returns("42");
10981113
yield return new TestCaseData("simpleInt.ToString().Length", onInstanceVariables, true).SetCategory("Instance Method,Instance Property").Returns(2);

CodingSeb.ExpressionEvaluator/CodingSeb.ExpressionEvaluator.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
<Product>CodingSeb.ExpressionEvaluator</Product>
66
<Description>A Simple Math and Pseudo C# Expression Evaluator in One C# File. Can also execute small C# like scripts</Description>
77
<Copyright>Copyright © Coding Seb 2017</Copyright>
8-
<Version>1.4.9.0</Version>
9-
<AssemblyVersion>1.4.9.0</AssemblyVersion>
10-
<FileVersion>1.4.9.0</FileVersion>
8+
<Version>1.4.10.0</Version>
9+
<AssemblyVersion>1.4.10.0</AssemblyVersion>
10+
<FileVersion>1.4.10.0</FileVersion>
1111
<OutputPath>bin\$(Configuration)\</OutputPath>
1212
<Authors>Coding Seb</Authors>
1313
<PackageId>CodingSeb.ExpressionEvaluator</PackageId>
@@ -18,7 +18,7 @@
1818
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1919
<PackageIconUrl>https://github.com/codingseb/ExpressionEvaluator/blob/master/Icon.png?raw=true</PackageIconUrl>
2020
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
21-
<PackageReleaseNotes>* You can now create SubExpression variables that are evaluate when met</PackageReleaseNotes>
21+
<PackageReleaseNotes>* Correction of some bugs linked to nullconditional operator</PackageReleaseNotes>
2222
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
2323
<RepositoryUrl>https://github.com/codingseb/ExpressionEvaluator</RepositoryUrl>
2424
</PropertyGroup>

CodingSeb.ExpressionEvaluator/ExpressionEvaluator.cs

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/******************************************************************************************************
22
Title : ExpressionEvaluator (https://github.com/codingseb/ExpressionEvaluator)
3-
Version : 1.4.9.0
3+
Version : 1.4.10.0
44
(if last digit (the forth) is not a zero, the version is an intermediate version and can be unstable)
55
66
Author : Coding Seb
@@ -212,28 +212,39 @@ protected enum TryBlockEvaluatedState
212212
protected virtual IList<ExpressionOperator> RightOperandOnlyOperatorsEvaluationDictionary => rightOperandOnlyOperatorsEvaluationDictionary;
213213
protected virtual IList<IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>>> OperatorsEvaluations => operatorsEvaluations;
214214

215+
protected static object IndexingOperatorFunc(dynamic left, dynamic right)
216+
{
217+
if (left is NullConditionalNullValue)
218+
return left;
219+
220+
Type type = ((object)left).GetType();
221+
222+
if (left is IDictionary<string, object> dictionaryLeft)
223+
{
224+
return dictionaryLeft[right];
225+
}
226+
else if (type.GetMethod("Item", new Type[] { ((object)right).GetType() }) is MethodInfo methodInfo)
227+
{
228+
return methodInfo.Invoke(left, new object[] { right });
229+
}
230+
231+
return left[right];
232+
}
233+
215234
protected static readonly IList<IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>>> operatorsEvaluations =
216235
new List<IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>>>()
217236
{
218237
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
219238
{
220-
{ExpressionOperator.Indexing, (dynamic left, dynamic right) =>
239+
{ExpressionOperator.Indexing, IndexingOperatorFunc},
240+
{ExpressionOperator.IndexingWithNullConditional, (dynamic left, dynamic right) =>
221241
{
222-
Type type = ((object)left).GetType();
223-
224-
if(left is IDictionary<string, object> dictionaryLeft)
225-
{
226-
return dictionaryLeft[right];
227-
}
228-
else if(type.GetMethod("Item", new Type[] { ((object)right).GetType() }) is MethodInfo methodInfo)
229-
{
230-
return methodInfo.Invoke(left, new object[] { right });
231-
}
242+
if(left == null)
243+
return new NullConditionalNullValue();
232244

233-
return left[right];
245+
return IndexingOperatorFunc(left, right);
234246
}
235247
},
236-
{ExpressionOperator.IndexingWithNullConditional, (dynamic left, dynamic right) => left is IDictionary<string,object> dictionaryLeft ? dictionaryLeft[right] : left?[right] },
237248
},
238249
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
239250
{
@@ -358,7 +369,6 @@ protected enum TryBlockEvaluatedState
358369
return null;
359370
}
360371
},
361-
//{ "if", (self, args) => (bool)self.Evaluate(args[0]) ? self.Evaluate(args[1]) : self.Evaluate(args[2]) },
362372
{ "in", (self, args) => args.Skip(1).ToList().ConvertAll(self.Evaluate).Contains(self.Evaluate(args[0])) },
363373
{ "List", (self, args) => args.ConvertAll(self.Evaluate) },
364374
{ "ListOfType", (self, args) =>
@@ -1787,7 +1797,7 @@ protected virtual bool EvaluateVarOrFunc(string expression, Stack<object> stack,
17871797
object obj = inObject ? stack.Pop() : Context;
17881798
object keepObj = obj;
17891799
Type objType = null;
1790-
Type[] inferedGenericsTypes = obj.GetType().GenericTypeArguments;
1800+
Type[] inferedGenericsTypes = obj?.GetType().GenericTypeArguments;
17911801
ValueTypeNestingTrace valueTypeNestingTrace = null;
17921802

17931803
if (obj != null && TypesToBlock.Contains(obj.GetType()))
@@ -1799,9 +1809,13 @@ protected virtual bool EvaluateVarOrFunc(string expression, Stack<object> stack,
17991809

18001810
try
18011811
{
1802-
if (varFuncMatch.Groups["nullConditional"].Success && obj == null)
1812+
if(obj is NullConditionalNullValue)
1813+
{
1814+
stack.Push(obj);
1815+
}
1816+
else if (varFuncMatch.Groups["nullConditional"].Success && obj == null)
18031817
{
1804-
stack.Push(null);
1818+
stack.Push(new NullConditionalNullValue());
18051819
}
18061820
else
18071821
{
@@ -1977,9 +1991,13 @@ protected virtual bool EvaluateVarOrFunc(string expression, Stack<object> stack,
19771991

19781992
try
19791993
{
1980-
if (varFuncMatch.Groups["nullConditional"].Success && obj == null)
1994+
if (obj is NullConditionalNullValue)
19811995
{
1982-
stack.Push(null);
1996+
stack.Push(obj);
1997+
}
1998+
else if (varFuncMatch.Groups["nullConditional"].Success && obj == null)
1999+
{
2000+
stack.Push(new NullConditionalNullValue());
19832001
}
19842002
else
19852003
{
@@ -2544,9 +2562,17 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
25442562
throw new Exception($"{bracketCount} ']' character {beVerb} missing in expression : [{expression}]");
25452563
}
25462564

2565+
dynamic left = stack.Pop();
2566+
2567+
if (left is NullConditionalNullValue)
2568+
{
2569+
stack.Push(left);
2570+
return true;
2571+
}
2572+
25472573
dynamic right = Evaluate(innerExp.ToString());
25482574
ExpressionOperator op = indexingBeginningMatch.Length == 2 ? ExpressionOperator.IndexingWithNullConditional : ExpressionOperator.Indexing;
2549-
dynamic left = stack.Pop();
2575+
25502576

25512577
if (OptionForceIntegerNumbersEvaluationsAsDoubleByDefault && right is double && Regex.IsMatch(innerExp.ToString(), @"^\d+$"))
25522578
right = (int)right;
@@ -2566,7 +2592,7 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
25662592
throw new ExpressionEvaluatorSyntaxErrorException($"The left part of {exceptionContext} must be a variable, a property or an indexer.");
25672593

25682594
if (op == ExpressionOperator.IndexingWithNullConditional)
2569-
throw new ExpressionEvaluatorSyntaxErrorException($"Null coalescing is not usable left to {exceptionContext}");
2595+
throw new ExpressionEvaluatorSyntaxErrorException($"Null conditional is not usable left to {exceptionContext}");
25702596

25712597
if (postFixOperator)
25722598
{
@@ -2752,6 +2778,7 @@ protected virtual object ProcessStack(Stack<object> stack)
27522778
List<object> list = stack
27532779
.Select(e => e is ValueTypeNestingTrace valueTypeNestingTrace ? valueTypeNestingTrace.Value : e)
27542780
.Select(e => e is SubExpression subExpression ? Evaluate(subExpression.Expression) : e)
2781+
.Select(e => e is NullConditionalNullValue ? null : e)
27552782
.ToList();
27562783

27572784
OperatorsEvaluations.ToList().ForEach((IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>> operatorEvalutationsDict) =>
@@ -3400,7 +3427,7 @@ protected virtual string GetCodeUntilEndOfStringInterpolation(string subExpr)
34003427

34013428
#endregion
34023429

3403-
#region Utils private sub classes for parsing and interpretation
3430+
#region Utils protected sub classes for parsing and interpretation
34043431

34053432
protected class ValueTypeNestingTrace
34063433
{
@@ -3424,6 +3451,9 @@ public void AssignValue()
34243451
}
34253452
}
34263453

3454+
protected class NullConditionalNullValue
3455+
{ }
3456+
34273457
protected class DelegateEncaps
34283458
{
34293459
private readonly InternalDelegate lambda;

0 commit comments

Comments
 (0)