Skip to content

Dev #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 4, 2020
Merged

Dev #52

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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