Skip to content

Commit bcc6a53

Browse files
committed
Correction of some bugs and add some tests
1 parent 2145483 commit bcc6a53

File tree

2 files changed

+94
-27
lines changed

2 files changed

+94
-27
lines changed

CodingSeb.ExpressionEvaluator.Tests/ExpressionEvaluatorTests.cs

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
using NUnit.Framework;
1+
using Newtonsoft.Json;
2+
using NUnit.Framework;
3+
using Shouldly;
24
using System;
35
using System.Collections.Generic;
46
using System.Text.RegularExpressions;
5-
using Shouldly;
6-
using Newtonsoft.Json;
7-
using System.Globalization;
8-
using System.Threading;
97

108
namespace CodingSeb.ExpressionEvaluator.Tests
119
{
@@ -1405,20 +1403,86 @@ public void ExceptionThrowingEvaluation(ExpressionEvaluator evaluator, string ex
14051403

14061404
#endregion
14071405

1408-
#region NumbersWithCommaDecimalSeparatorCulture
1406+
#region EvaluateWithSpecificEvaluator
1407+
1408+
#region TestCasesEvaluateWithSpecificEvaluator
14091409

1410-
[TestCase("0,5", ",", ";", ExpectedResult = 0.5, Category = "Numbers")]
1411-
[TestCase("0'5", "'", ",", ExpectedResult = 0.5, Category = "Numbers")]
1412-
[TestCase("0.5", ".", ",", ExpectedResult = 0.5, Category = "Numbers")]
1413-
[TestCase("Max(0,5; 0,7)", ",", ";", ExpectedResult = 0.7, Category = "Numbers")]
1414-
public object NumbersDecimalSeparatorAndFunctionArgsSeparators(string expression, string decimalSeparator, string functionArgsSeparator)
1410+
public static IEnumerable<TestCaseData> TestCasesEvaluateWithSpecificEvaluator
14151411
{
1416-
ExpressionEvaluator evaluator = new ExpressionEvaluator
1412+
get
14171413
{
1418-
OptionNumberParsingDecimalSeparator = decimalSeparator,
1419-
OptionFunctionArgumentsSeparator = functionArgsSeparator
1420-
};
1414+
#region Different culture for numbers
14211415

1416+
yield return new TestCaseData(new ExpressionEvaluator
1417+
{
1418+
OptionNumberParsingDecimalSeparator = ",",
1419+
}
1420+
, "0,5")
1421+
.Returns(0.5)
1422+
.SetCategory("Options")
1423+
.SetCategory("Numbers Culture");
1424+
1425+
yield return new TestCaseData(new ExpressionEvaluator
1426+
{
1427+
OptionNumberParsingDecimalSeparator = "'",
1428+
}
1429+
, "0'5")
1430+
.Returns(0.5)
1431+
.SetCategory("Options")
1432+
.SetCategory("Numbers Culture");
1433+
1434+
yield return new TestCaseData(new ExpressionEvaluator
1435+
{
1436+
OptionNumberParsingDecimalSeparator = ".",
1437+
}
1438+
, "0.5")
1439+
.Returns(0.5)
1440+
.SetCategory("Options")
1441+
.SetCategory("Numbers Culture");
1442+
1443+
yield return new TestCaseData(new ExpressionEvaluator
1444+
{
1445+
OptionNumberParsingDecimalSeparator = ",",
1446+
OptionFunctionArgumentsSeparator = ";"
1447+
}
1448+
, "Max(0,5; 0,7)")
1449+
.Returns(0.7)
1450+
.SetCategory("Options")
1451+
.SetCategory("Numbers Culture");
1452+
1453+
yield return new TestCaseData(new ExpressionEvaluator
1454+
{
1455+
OptionNumberParsingDecimalSeparator = ",",
1456+
OptionNumberParsingThousandSeparator = "'",
1457+
OptionCharEvaluationActive = false,
1458+
OptionFunctionArgumentsSeparator = ";"
1459+
}
1460+
, "Max(1'200,5; 1'111'000,7)")
1461+
.Returns(1111000.7)
1462+
.SetCategory("Options")
1463+
.SetCategory("Numbers Culture");
1464+
1465+
yield return new TestCaseData(new ExpressionEvaluator
1466+
{
1467+
OptionNumberParsingDecimalSeparator = ",",
1468+
OptionNumberParsingThousandSeparator = "'",
1469+
OptionCharEvaluationActive = false,
1470+
OptionInitializersSeparator = ";"
1471+
}
1472+
, "new double[]{1'200,5; 1'111'000,7}.Max()")
1473+
.Returns(1111000.7)
1474+
.SetCategory("Options")
1475+
.SetCategory("Numbers Culture");
1476+
1477+
#endregion
1478+
}
1479+
}
1480+
1481+
#endregion
1482+
1483+
[TestCaseSource(nameof(TestCasesEvaluateWithSpecificEvaluator))]
1484+
public object EvaluateWithSpecificEvaluator(ExpressionEvaluator evaluator, string expression)
1485+
{
14221486
return evaluator.Evaluate(expression);
14231487
}
14241488

CodingSeb.ExpressionEvaluator/ExpressionEvaluator.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class ExpressionEvaluator
3333

3434
private static readonly Regex varOrFunctionRegEx = new Regex($@"^((?<sign>[+-])|(?<prefixOperator>[+][+]|--)|(?<inObject>(?<nullConditional>[?])?\.)?)(?<name>[{ diactiticsKeywordsRegexPattern }][{ diactiticsKeywordsRegexPattern }0-9]*)\s*((?<assignationOperator>(?<assignmentPrefix>[+\-*/%&|^]|<<|>>)?=(?![=>]))|(?<postfixOperator>([+][+]|--)(?![{ diactiticsKeywordsRegexPattern}0-9]))|((?<isgeneric>[<](?>[^<>]+|(?<gentag>[<])|(?<-gentag>[>]))*(?(gentag)(?!))[>])?(?<isfunction>[(])?))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
3535

36-
private string numberRegexPattern = @"^(?<sign>[+-])?([0-9][0-9_{1}]*[0-9]|\d)(?<hasdecimal>{0}?([0-9][0-9_]*[0-9]|\d)(e[+-]?([0-9][0-9_]*[0-9]|\d))?)?(?<type>ul|[fdulm])?";
36+
private readonly string numberRegexPattern = @"^(?<sign>[+-])?([0-9][0-9_{1}]*[0-9]|\d)(?<hasdecimal>{0}?([0-9][0-9_]*[0-9]|\d)(e[+-]?([0-9][0-9_]*[0-9]|\d))?)?(?<type>ul|[fdulm])?";
3737
private Regex numberRegex = null;
3838

3939
private static readonly Regex otherBasesNumberRegex = new Regex(@"^(?<sign>[+-])?(?<value>0(?<type>x)([0-9a-f][0-9a-f_]*[0-9a-f]|[0-9a-f])|0(?<type>b)([01][01_]*[01]|[01]))", RegexOptions.IgnoreCase);
@@ -534,8 +534,6 @@ private StringComparer StringComparerForCasing
534534

535535
private CultureInfo cultureInfoForNumberParsing = CultureInfo.InvariantCulture.Clone() as CultureInfo;
536536

537-
private string optionNumberParsingDecimalSeparator = ".";
538-
539537
/// <summary>
540538
/// The culture used to evaluate numbers
541539
/// Synchronized with OptionNumberParsingDecimalSeparator and OptionNumberParsingThousandSeparator.
@@ -558,6 +556,8 @@ public CultureInfo CultureInfoForNumberParsing
558556
}
559557
}
560558

559+
private string optionNumberParsingDecimalSeparator = ".";
560+
561561
/// <summary>
562562
/// Allow to change the decimal separator of numbers when parsing expressions.
563563
/// By default "."
@@ -567,17 +567,22 @@ public CultureInfo CultureInfoForNumberParsing
567567
public string OptionNumberParsingDecimalSeparator
568568
{
569569

570-
get => optionNumberParsingDecimalSeparator;
570+
get
571+
{
572+
return optionNumberParsingDecimalSeparator;
573+
}
571574

572575
set
573576
{
574-
optionNumberParsingDecimalSeparator = value;
575-
CultureInfoForNumberParsing.NumberFormat.NumberDecimalSeparator = value;
577+
optionNumberParsingDecimalSeparator = value ?? ".";
578+
CultureInfoForNumberParsing.NumberFormat.NumberDecimalSeparator = optionNumberParsingDecimalSeparator;
576579

577580
numberRegex = new Regex(string.Format(numberRegexPattern, Regex.Escape(optionNumberParsingDecimalSeparator), Regex.Escape(optionNumberParsingThousandSeparator)), RegexOptions.IgnoreCase);
578581
}
579582
}
580583

584+
private string optionNumberParsingThousandSeparator = string.Empty;
585+
581586
/// <summary>
582587
/// Allow to change the thousand separator of numbers when parsing expressions.
583588
/// By default string.Empty
@@ -593,7 +598,7 @@ public string OptionNumberParsingThousandSeparator
593598

594599
set
595600
{
596-
optionNumberParsingThousandSeparator = value;
601+
optionNumberParsingThousandSeparator = value ?? string.Empty;
597602
CultureInfoForNumberParsing.NumberFormat.NumberGroupSeparator = value;
598603

599604
numberRegex = new Regex(string.Format(numberRegexPattern, Regex.Escape(optionNumberParsingDecimalSeparator), Regex.Escape(optionNumberParsingThousandSeparator)), RegexOptions.IgnoreCase);
@@ -832,7 +837,7 @@ public ExpressionEvaluator()
832837
{
833838
Assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies());
834839
instanceCreationWithNewKeywordRegex = new Regex(InstanceCreationWithNewKeywordRegexPattern);
835-
numberRegex = new Regex(string.Format(numberRegexPattern, @"\."), RegexOptions.IgnoreCase);
840+
numberRegex = new Regex(string.Format(numberRegexPattern, @"\.", string.Empty), RegexOptions.IgnoreCase);
836841
CultureInfoForNumberParsing.NumberFormat.NumberDecimalSeparator = ".";
837842
castRegex = new Regex(CastRegexPattern);
838843
}
@@ -851,7 +856,6 @@ public ExpressionEvaluator(Dictionary<string, object> variables) : this()
851856
#region Main evaluate methods (Expressions and scripts ==> public)
852857

853858
private bool inScript = false;
854-
private string optionNumberParsingThousandSeparator;
855859

856860
/// <summary>
857861
/// Evaluate a script (multiple expressions separated by semicolon)
@@ -1653,7 +1657,6 @@ void Init(object element, List<string> initArgs)
16531657
i += initInNewBeginningMatch.Length;
16541658

16551659
List<string> arrayElements = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets(expr, ref i, true, OptionInitializersSeparator, "{", "}");
1656-
i++;
16571660

16581661
if (array == null)
16591662
array = Array.CreateInstance(type, arrayElements.Count);
@@ -2790,13 +2793,13 @@ private List<string> GetExpressionsBetweenParenthesesOrOtherImbricableBrackets(s
27902793
Match internalStringMatch = stringBeginningRegex.Match(subExpr);
27912794
Match internalCharMatch = internalCharRegex.Match(subExpr);
27922795

2793-
if (internalStringMatch.Success)
2796+
if (OptionStringEvaluationActive && internalStringMatch.Success)
27942797
{
27952798
string innerString = internalStringMatch.Value + GetCodeUntilEndOfString(expr.Substring(i + internalStringMatch.Length), internalStringMatch);
27962799
currentExpression += innerString;
27972800
i += innerString.Length - 1;
27982801
}
2799-
else if (internalCharMatch.Success)
2802+
else if (OptionCharEvaluationActive && internalCharMatch.Success)
28002803
{
28012804
currentExpression += internalCharMatch.Value;
28022805
i += internalCharMatch.Length - 1;

0 commit comments

Comments
 (0)