Skip to content

Commit

Permalink
Merge pull request #52 from codingseb/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
codingseb authored Jun 4, 2020
2 parents 6cda5e0 + f4110a5 commit c2f7d2c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 27 deletions.
15 changes: 15 additions & 0 deletions CodingSeb.ExpressionEvaluator.Tests/ExpressionEvaluatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,10 @@ public void TypeTesting(string expression, Type type)
[TestCase("null ?? \"Option2\"", TestOf = typeof(string), ExpectedResult = "Option2", Category = "Null Coalescing Operator")]
#endregion

#region Null conditional Operator
[TestCase("null?.Trim()", ExpectedResult = null, Category = "Null conditional Operator")]
#endregion

#region default values
[TestCase("default(int)", TestOf = typeof(int), ExpectedResult = 0, Category = "default values")]
[TestCase("default(bool)", TestOf = typeof(bool), ExpectedResult = false, Category = "default values")]
Expand Down Expand Up @@ -1075,9 +1079,11 @@ public static IEnumerable<TestCaseData> TestCasesForWithCustomVariablesExpressio
Dictionary<string, object> onInstanceVariables = new Dictionary<string, object>()
{
{ "simpleArray", new object[] {2 , "Hello", true} },
{ "simpleArray2", new object[] {2 , " Hello ", true, null } },
{ "otherArray", new object[] {2 , "Hello", true, new ClassForTest1() { IntProperty = 18 } } },
{ "simpleList", new List<object>() {"Test" ,false, -15, 123.8f} },
{ "nullVar", null },
{ "notTrimmedString", " Hello " },
{ "simpleInt", 42 },
{ "simpleChar", 'n' },
{ "simpleLineFeed", '\n' },
Expand All @@ -1090,9 +1096,18 @@ public static IEnumerable<TestCaseData> TestCasesForWithCustomVariablesExpressio
yield return new TestCaseData("simpleList?.Count", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Property").Returns(4);
yield return new TestCaseData("nullVar?.Length", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Property").Returns(null);
yield return new TestCaseData("nullVar?.Count", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Property").Returns(null);
yield return new TestCaseData("nullVar?.Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Method").Returns(null);
yield return new TestCaseData("nullVar?.Trim().Length", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Method").Returns(null);
yield return new TestCaseData("notTrimmedString?.Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Method").Returns("Hello");
yield return new TestCaseData("notTrimmedString?.Trim().Length", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional Method").Returns(5);
yield return new TestCaseData("simpleArray?[2]", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(true);
yield return new TestCaseData("simpleList?[2]", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(-15);
yield return new TestCaseData("simpleArray2[1].Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns("Hello");
yield return new TestCaseData("nullVar?[2]", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
yield return new TestCaseData("nullVar?[1].Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
yield return new TestCaseData("nullVar?[1]?.Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
yield return new TestCaseData("nullVar?[1][3]", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);
yield return new TestCaseData("simpleArray2?[3]?.Trim()", onInstanceVariables, true).SetCategory("Instance Property,Null Conditional indexing").Returns(null);

yield return new TestCaseData("simpleInt.ToString()", onInstanceVariables, true).SetCategory("Instance Method").Returns("42");
yield return new TestCaseData("simpleInt.ToString().Length", onInstanceVariables, true).SetCategory("Instance Method,Instance Property").Returns(2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
<Product>CodingSeb.ExpressionEvaluator</Product>
<Description>A Simple Math and Pseudo C# Expression Evaluator in One C# File. Can also execute small C# like scripts</Description>
<Copyright>Copyright © Coding Seb 2017</Copyright>
<Version>1.4.9.0</Version>
<AssemblyVersion>1.4.9.0</AssemblyVersion>
<FileVersion>1.4.9.0</FileVersion>
<Version>1.4.10.0</Version>
<AssemblyVersion>1.4.10.0</AssemblyVersion>
<FileVersion>1.4.10.0</FileVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<Authors>Coding Seb</Authors>
<PackageId>CodingSeb.ExpressionEvaluator</PackageId>
Expand All @@ -18,7 +18,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageIconUrl>https://github.com/codingseb/ExpressionEvaluator/blob/master/Icon.png?raw=true</PackageIconUrl>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageReleaseNotes>* You can now create SubExpression variables that are evaluate when met</PackageReleaseNotes>
<PackageReleaseNotes>* Correction of some bugs linked to nullconditional operator</PackageReleaseNotes>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<RepositoryUrl>https://github.com/codingseb/ExpressionEvaluator</RepositoryUrl>
</PropertyGroup>
Expand Down
76 changes: 53 additions & 23 deletions CodingSeb.ExpressionEvaluator/ExpressionEvaluator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/******************************************************************************************************
Title : ExpressionEvaluator (https://github.com/codingseb/ExpressionEvaluator)
Version : 1.4.9.0
Version : 1.4.10.0
(if last digit (the forth) is not a zero, the version is an intermediate version and can be unstable)
Author : Coding Seb
Expand Down Expand Up @@ -212,28 +212,39 @@ protected enum TryBlockEvaluatedState
protected virtual IList<ExpressionOperator> RightOperandOnlyOperatorsEvaluationDictionary => rightOperandOnlyOperatorsEvaluationDictionary;
protected virtual IList<IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>>> OperatorsEvaluations => operatorsEvaluations;

protected static object IndexingOperatorFunc(dynamic left, dynamic right)
{
if (left is NullConditionalNullValue)
return left;

Type type = ((object)left).GetType();

if (left is IDictionary<string, object> dictionaryLeft)
{
return dictionaryLeft[right];
}
else if (type.GetMethod("Item", new Type[] { ((object)right).GetType() }) is MethodInfo methodInfo)
{
return methodInfo.Invoke(left, new object[] { right });
}

return left[right];
}

protected static readonly IList<IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>>> operatorsEvaluations =
new List<IDictionary<ExpressionOperator, Func<dynamic, dynamic, object>>>()
{
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
{
{ExpressionOperator.Indexing, (dynamic left, dynamic right) =>
{ExpressionOperator.Indexing, IndexingOperatorFunc},
{ExpressionOperator.IndexingWithNullConditional, (dynamic left, dynamic right) =>
{
Type type = ((object)left).GetType();

if(left is IDictionary<string, object> dictionaryLeft)
{
return dictionaryLeft[right];
}
else if(type.GetMethod("Item", new Type[] { ((object)right).GetType() }) is MethodInfo methodInfo)
{
return methodInfo.Invoke(left, new object[] { right });
}
if(left == null)
return new NullConditionalNullValue();

return left[right];
return IndexingOperatorFunc(left, right);
}
},
{ExpressionOperator.IndexingWithNullConditional, (dynamic left, dynamic right) => left is IDictionary<string,object> dictionaryLeft ? dictionaryLeft[right] : left?[right] },
},
new Dictionary<ExpressionOperator, Func<dynamic, dynamic, object>>()
{
Expand Down Expand Up @@ -358,7 +369,6 @@ protected enum TryBlockEvaluatedState
return null;
}
},
//{ "if", (self, args) => (bool)self.Evaluate(args[0]) ? self.Evaluate(args[1]) : self.Evaluate(args[2]) },
{ "in", (self, args) => args.Skip(1).ToList().ConvertAll(self.Evaluate).Contains(self.Evaluate(args[0])) },
{ "List", (self, args) => args.ConvertAll(self.Evaluate) },
{ "ListOfType", (self, args) =>
Expand Down Expand Up @@ -1787,7 +1797,7 @@ protected virtual bool EvaluateVarOrFunc(string expression, Stack<object> stack,
object obj = inObject ? stack.Pop() : Context;
object keepObj = obj;
Type objType = null;
Type[] inferedGenericsTypes = obj.GetType().GenericTypeArguments;
Type[] inferedGenericsTypes = obj?.GetType().GenericTypeArguments;
ValueTypeNestingTrace valueTypeNestingTrace = null;

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

try
{
if (varFuncMatch.Groups["nullConditional"].Success && obj == null)
if(obj is NullConditionalNullValue)
{
stack.Push(obj);
}
else if (varFuncMatch.Groups["nullConditional"].Success && obj == null)
{
stack.Push(null);
stack.Push(new NullConditionalNullValue());
}
else
{
Expand Down Expand Up @@ -1977,9 +1991,13 @@ protected virtual bool EvaluateVarOrFunc(string expression, Stack<object> stack,

try
{
if (varFuncMatch.Groups["nullConditional"].Success && obj == null)
if (obj is NullConditionalNullValue)
{
stack.Push(null);
stack.Push(obj);
}
else if (varFuncMatch.Groups["nullConditional"].Success && obj == null)
{
stack.Push(new NullConditionalNullValue());
}
else
{
Expand Down Expand Up @@ -2544,9 +2562,17 @@ protected virtual bool EvaluateIndexing(string expression, Stack<object> stack,
throw new Exception($"{bracketCount} ']' character {beVerb} missing in expression : [{expression}]");
}

dynamic left = stack.Pop();

if (left is NullConditionalNullValue)
{
stack.Push(left);
return true;
}

dynamic right = Evaluate(innerExp.ToString());
ExpressionOperator op = indexingBeginningMatch.Length == 2 ? ExpressionOperator.IndexingWithNullConditional : ExpressionOperator.Indexing;
dynamic left = stack.Pop();


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

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

if (postFixOperator)
{
Expand Down Expand Up @@ -2752,6 +2778,7 @@ protected virtual object ProcessStack(Stack<object> stack)
List<object> list = stack
.Select(e => e is ValueTypeNestingTrace valueTypeNestingTrace ? valueTypeNestingTrace.Value : e)
.Select(e => e is SubExpression subExpression ? Evaluate(subExpression.Expression) : e)
.Select(e => e is NullConditionalNullValue ? null : e)
.ToList();

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

#endregion

#region Utils private sub classes for parsing and interpretation
#region Utils protected sub classes for parsing and interpretation

protected class ValueTypeNestingTrace
{
Expand All @@ -3424,6 +3451,9 @@ public void AssignValue()
}
}

protected class NullConditionalNullValue
{ }

protected class DelegateEncaps
{
private readonly InternalDelegate lambda;
Expand Down

0 comments on commit c2f7d2c

Please sign in to comment.