Skip to content

Commit c273809

Browse files
committed
On the fly indexing OK with tests
1 parent 417fa9a commit c273809

File tree

2 files changed

+100
-61
lines changed

2 files changed

+100
-61
lines changed

CodingSeb.ExpressionEvaluator.Tests/ExpressionEvaluatorTests.cs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Shouldly;
44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using System.Text.RegularExpressions;
78

89
namespace CodingSeb.ExpressionEvaluator.Tests
@@ -1284,9 +1285,9 @@ private void Evaluator_EvaluateVariable(object sender, VariableEvaluationEventAr
12841285
}
12851286
}
12861287

1287-
[TestCase("ClassForTest1.Add(1, 5)", ExpectedResult = 6, Category = "On the fly method")]
1288-
[TestCase("ClassForTest1.Add(1, 5.0)", ExpectedResult = 6, Category = "On the fly method")]
1289-
public object OnTheFlyEvaluation2(string expression)
1288+
[TestCase("ClassForTest1.Add(1, 5)", ExpectedResult = 6)]
1289+
[TestCase("ClassForTest1.Add(1, 5.0)", ExpectedResult = 6)]
1290+
public object OnTheFlyCastEvaluation(string expression)
12901291
{
12911292
ExpressionEvaluator evaluator = new ExpressionEvaluator(new ContextObject1());
12921293

@@ -1310,6 +1311,27 @@ private void Evaluator_EvaluateParameterCast(object sender, ParameterCastEvaluat
13101311
}
13111312
}
13121313

1314+
[TestCase("2[\"Test\"]", ExpectedResult = "Test,Test")]
1315+
[TestCase("3[\"Hello\"]", ExpectedResult = "Hello,Hello,Hello")]
1316+
public object OnTheFlyIndexingEvaluation(string expression)
1317+
{
1318+
ExpressionEvaluator evaluator = new ExpressionEvaluator(new ContextObject1());
1319+
1320+
evaluator.PreEvaluateIndexing += Evaluator_PreEvaluateIndexing;
1321+
1322+
evaluator.Namespaces.Add("CodingSeb.ExpressionEvaluator.Tests");
1323+
1324+
return evaluator.Evaluate(expression);
1325+
}
1326+
1327+
private void Evaluator_PreEvaluateIndexing(object sender, IndexingPreEvaluationEventArg e)
1328+
{
1329+
if(e.This is int intValue && e.EvaluateArg() is string text)
1330+
{
1331+
e.Value = string.Join(",", Enumerable.Repeat(text, intValue));
1332+
}
1333+
}
1334+
13131335
#endregion
13141336

13151337
#endregion
@@ -1499,6 +1521,7 @@ public static IEnumerable<TestCaseData> TestCasesForExceptionThrowingEvaluation
14991521
{ "P1var", "P1" },
15001522
{ "myObj", new ClassForTest1() },
15011523
{ "nullVar", null },
1524+
{ "myArray", new int[] {1, 2, 3} },
15021525
});
15031526

15041527
evaluator.PreEvaluateVariable += (sender, e) =>
@@ -1513,11 +1536,18 @@ public static IEnumerable<TestCaseData> TestCasesForExceptionThrowingEvaluation
15131536
e.CancelEvaluation = true;
15141537
};
15151538

1539+
evaluator.PreEvaluateIndexing += (sender, e) =>
1540+
{
1541+
if (e.This is int[])
1542+
e.CancelEvaluation = true;
1543+
};
1544+
15161545
yield return new TestCaseData(evaluator, "Pi", typeof(ExpressionEvaluatorSyntaxErrorException)).SetCategory("OnTheFly canceled Var");
15171546
yield return new TestCaseData(evaluator, "P1var", typeof(ExpressionEvaluatorSyntaxErrorException)).SetCategory("OnTheFly canceled Var");
15181547
yield return new TestCaseData(evaluator, "myObj.PropertyThatWillFailed", typeof(ExpressionEvaluatorSyntaxErrorException)).SetCategory("OnTheFly canceled Var");
15191548
yield return new TestCaseData(evaluator, "myObj.Add3To(5)", typeof(ExpressionEvaluatorSyntaxErrorException)).SetCategory("OnTheFly canceled Func");
15201549
yield return new TestCaseData(evaluator, "Abs(-5)", typeof(ExpressionEvaluatorSyntaxErrorException)).SetCategory("OnTheFly canceled Func");
1550+
yield return new TestCaseData(evaluator, "myArray[1]", typeof(ExpressionEvaluatorSyntaxErrorException)).SetCategory("OnTheFlyCanceledIndexing");
15211551
#endregion
15221552

15231553
#region Bugs corrections
@@ -1536,7 +1566,20 @@ public static IEnumerable<TestCaseData> TestCasesForExceptionThrowingEvaluation
15361566
[TestCaseSource(nameof(TestCasesForExceptionThrowingEvaluation))]
15371567
public void ExceptionThrowingEvaluation(ExpressionEvaluator evaluator, string expression, Type exceptionType)
15381568
{
1539-
Assert.Catch(exceptionType, () => evaluator.Evaluate(expression));
1569+
Exception e = null;
1570+
object result = null;
1571+
1572+
try
1573+
{
1574+
result = evaluator.Evaluate(expression);
1575+
}
1576+
catch(Exception exception)
1577+
{
1578+
e = exception;
1579+
}
1580+
1581+
result.ShouldBeNull();
1582+
e.ShouldNotBeNull().ShouldBeOfType(exceptionType);
15401583
}
15411584

15421585
#endregion

CodingSeb.ExpressionEvaluator/ExpressionEvaluator.cs

Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,7 @@ protected virtual BindingFlags StaticBindingFlag
903903

904904
#endregion
905905

906-
#region Custom and on the fly variables and methods
906+
#region Custom and on the fly evaluation
907907

908908
/// <summary>
909909
/// If set, this object is used to use it's fields, properties and methods as global variables and functions
@@ -959,12 +959,6 @@ public IDictionary<string, object> Variables
959959
/// </summary>
960960
public event EventHandler<FunctionEvaluationEventArg> EvaluateFunction;
961961

962-
/// <summary>
963-
/// Is fired if no indexing were found.
964-
/// Allow to define an indexing and the corresponding value on the fly.
965-
/// </summary>
966-
public event EventHandler<IndexingEvaluationEventArg> EvaluateIndexing;
967-
968962
/// <summary>
969963
/// Is fired when a parameter is not of the correct type for the function.
970964
/// Allow to define a custom parameter cast to make the function call work on the fly.
@@ -1554,7 +1548,7 @@ public T Evaluate<T>(string expression)
15541548
EvaluateOperators,
15551549
EvaluateChar,
15561550
EvaluateParenthis,
1557-
EvaluateIndexingOperator,
1551+
EvaluateIndexing,
15581552
EvaluateString,
15591553
EvaluateTernaryConditionalOperator,
15601554
});
@@ -2644,7 +2638,7 @@ protected virtual void CorrectStackWithUnaryPlusOrMinusBeforeParenthisIfNecessar
26442638
}
26452639
}
26462640

2647-
protected virtual bool EvaluateIndexingOperator(string expression, Stack<object> stack, ref int i)
2641+
protected virtual bool EvaluateIndexing(string expression, Stack<object> stack, ref int i)
26482642
{
26492643
if (!OptionIndexingActive)
26502644
return false;
@@ -2700,58 +2694,69 @@ protected virtual bool EvaluateIndexingOperator(string expression, Stack<object>
27002694
return true;
27012695
}
27022696

2703-
//IndexingPreEvaluationEventArg indexingPreEvaluationEventArg = new IndexingPreEvaluationEventArg(innerExp.ToString(), this, left);
2697+
IndexingPreEvaluationEventArg indexingPreEvaluationEventArg = new IndexingPreEvaluationEventArg(innerExp.ToString(), this, left);
27042698

2705-
//PreEvaluateIndexing?.Invoke(this, indexingPreEvaluationEventArg);
2699+
PreEvaluateIndexing?.Invoke(this, indexingPreEvaluationEventArg);
27062700

2707-
dynamic right = Evaluate(innerExp.ToString());
2708-
ExpressionOperator op = indexingBeginningMatch.Length == 2 ? ExpressionOperator.IndexingWithNullConditional : ExpressionOperator.Indexing;
2701+
if (indexingPreEvaluationEventArg.CancelEvaluation)
2702+
{
2703+
throw new ExpressionEvaluatorSyntaxErrorException($"[{left.GetType()}] can not be indexed.");
2704+
}
2705+
else if (indexingPreEvaluationEventArg.HasValue)
2706+
{
2707+
stack.Push(indexingPreEvaluationEventArg.Value);
2708+
}
2709+
else
2710+
{
2711+
dynamic right = Evaluate(innerExp.ToString());
2712+
ExpressionOperator op = indexingBeginningMatch.Length == 2 ? ExpressionOperator.IndexingWithNullConditional : ExpressionOperator.Indexing;
27092713

2710-
if (OptionForceIntegerNumbersEvaluationsAsDoubleByDefault && right is double && Regex.IsMatch(innerExp.ToString(), @"^\d+$"))
2711-
right = (int)right;
2714+
if (OptionForceIntegerNumbersEvaluationsAsDoubleByDefault && right is double && Regex.IsMatch(innerExp.ToString(), @"^\d+$"))
2715+
right = (int)right;
27122716

2713-
Match assignationOrPostFixOperatorMatch = null;
2717+
Match assignationOrPostFixOperatorMatch = null;
27142718

2715-
dynamic valueToPush = null;
2719+
dynamic valueToPush = null;
27162720

2717-
if (OptionIndexingAssignationActive && (assignationOrPostFixOperatorMatch = assignationOrPostFixOperatorRegex.Match(expression.Substring(i + 1))).Success)
2718-
{
2719-
i += assignationOrPostFixOperatorMatch.Length + 1;
2721+
if (OptionIndexingAssignationActive && (assignationOrPostFixOperatorMatch = assignationOrPostFixOperatorRegex.Match(expression.Substring(i + 1))).Success)
2722+
{
2723+
i += assignationOrPostFixOperatorMatch.Length + 1;
27202724

2721-
bool postFixOperator = assignationOrPostFixOperatorMatch.Groups["postfixOperator"].Success;
2722-
string exceptionContext = postFixOperator ? "++ or -- operator" : "an assignation";
2725+
bool postFixOperator = assignationOrPostFixOperatorMatch.Groups["postfixOperator"].Success;
2726+
string exceptionContext = postFixOperator ? "++ or -- operator" : "an assignation";
27232727

2724-
if (stack.Count > 1)
2725-
throw new ExpressionEvaluatorSyntaxErrorException($"The left part of {exceptionContext} must be a variable, a property or an indexer.");
2728+
if (stack.Count > 1)
2729+
throw new ExpressionEvaluatorSyntaxErrorException($"The left part of {exceptionContext} must be a variable, a property or an indexer.");
27262730

2727-
if (op == ExpressionOperator.IndexingWithNullConditional)
2728-
throw new ExpressionEvaluatorSyntaxErrorException($"Null conditional is not usable left to {exceptionContext}");
2731+
if (op == ExpressionOperator.IndexingWithNullConditional)
2732+
throw new ExpressionEvaluatorSyntaxErrorException($"Null conditional is not usable left to {exceptionContext}");
27292733

2730-
if (postFixOperator)
2731-
{
2732-
if (left is IDictionary<string, object> dictionaryLeft)
2733-
valueToPush = assignationOrPostFixOperatorMatch.Groups["postfixOperator"].Value.Equals("++") ? dictionaryLeft[right]++ : dictionaryLeft[right]--;
2734+
if (postFixOperator)
2735+
{
2736+
if (left is IDictionary<string, object> dictionaryLeft)
2737+
valueToPush = assignationOrPostFixOperatorMatch.Groups["postfixOperator"].Value.Equals("++") ? dictionaryLeft[right]++ : dictionaryLeft[right]--;
2738+
else
2739+
valueToPush = assignationOrPostFixOperatorMatch.Groups["postfixOperator"].Value.Equals("++") ? left[right]++ : left[right]--;
2740+
}
27342741
else
2735-
valueToPush = assignationOrPostFixOperatorMatch.Groups["postfixOperator"].Value.Equals("++") ? left[right]++ : left[right]--;
2742+
{
2743+
valueToPush = ManageKindOfAssignation(expression, ref i, assignationOrPostFixOperatorMatch, () => OperatorsEvaluations[0][op](left, right));
2744+
2745+
if (left is IDictionary<string, object> dictionaryLeft)
2746+
dictionaryLeft[right] = valueToPush;
2747+
else
2748+
left[right] = valueToPush;
2749+
2750+
stack.Clear();
2751+
}
27362752
}
27372753
else
27382754
{
2739-
valueToPush = ManageKindOfAssignation(expression, ref i, assignationOrPostFixOperatorMatch, () => OperatorsEvaluations[0][op](left, right));
2740-
2741-
if (left is IDictionary<string, object> dictionaryLeft)
2742-
dictionaryLeft[right] = valueToPush;
2743-
else
2744-
left[right] = valueToPush;
2745-
2746-
stack.Clear();
2755+
valueToPush = OperatorsEvaluations[0][op](left, right);
27472756
}
2748-
}
2749-
else
2750-
{
2751-
valueToPush = OperatorsEvaluations[0][op](left, right);
2752-
}
27532757

2754-
stack.Push(valueToPush);
2758+
stack.Push(valueToPush);
2759+
}
27552760

27562761
return true;
27572762
}
@@ -4428,12 +4433,13 @@ public FunctionPreEvaluationEventArg(string name, List<string> args = null, Expr
44284433
public bool CancelEvaluation { get; set; }
44294434
}
44304435

4436+
44314437
/// <summary>
44324438
/// Infos about the indexing that is currently evaluate
44334439
/// </summary>
4434-
public partial class IndexingEvaluationEventArg : EventArgs
4440+
public partial class IndexingPreEvaluationEventArg : EventArgs
44354441
{
4436-
public IndexingEvaluationEventArg(string arg, ExpressionEvaluator evaluator, object onInstance)
4442+
public IndexingPreEvaluationEventArg(string arg, ExpressionEvaluator evaluator, object onInstance)
44374443
{
44384444
Arg = arg;
44394445
This = onInstance;
@@ -4492,16 +4498,6 @@ public T EvaluateArg<T>()
44924498
{
44934499
return Evaluator.Evaluate<T>(Arg);
44944500
}
4495-
}
4496-
4497-
/// <summary>
4498-
/// Infos about the indexing that is currently evaluate
4499-
/// </summary>
4500-
public partial class IndexingPreEvaluationEventArg : IndexingEvaluationEventArg
4501-
{
4502-
public IndexingPreEvaluationEventArg(string arg, ExpressionEvaluator evaluator, object onInstance)
4503-
: base(arg, evaluator, onInstance)
4504-
{ }
45054501

45064502
/// <summary>
45074503
/// If set to true cancel the evaluation of the current function or method and throw an exception that the function does not exists

0 commit comments

Comments
 (0)