@@ -32,7 +32,10 @@ public class ExpressionEvaluator
32
32
private static readonly string diactiticsKeywordsRegexPattern = "a-zA-Z_" + diactitics ;
33
33
34
34
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 ) ;
35
- private static readonly Regex numberRegex = new Regex ( @"^(?<sign>[+-])?([0-9][0-9_]*[0-9]|\d)(?<hasdecimal>\.?([0-9][0-9_]*[0-9]|\d)(e[+-]?([0-9][0-9_]*[0-9]|\d))?)?(?<type>ul|[fdulm])?" , RegexOptions . IgnoreCase ) ;
35
+
36
+ private string numberRegexPattern = @"^(?<sign>[+-])?([0-9][0-9_]*[0-9]|\d)(?<hasdecimal>{0}?([0-9][0-9_]*[0-9]|\d)(e[+-]?([0-9][0-9_]*[0-9]|\d))?)?(?<type>ul|[fdulm])?" ;
37
+ private Regex numberRegex = null ;
38
+
36
39
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 ) ;
37
40
private static readonly Regex stringBeginningRegex = new Regex ( "^(?<interpolated>[$])?(?<escaped>[@])?[\" ]" ) ;
38
41
private static readonly Regex internalCharRegex = new Regex ( @"^['](\\[']|[^'])*[']" ) ;
@@ -163,14 +166,14 @@ private enum TryBlockEvaluatedState
163
166
{ "void" , typeof ( void ) }
164
167
} ;
165
168
166
- private static Dictionary < string , Func < string , object > > numberSuffixToParse = new Dictionary < string , Func < string , object > > ( StringComparer . OrdinalIgnoreCase ) // Always Case insensitive, like in C#
169
+ private static Dictionary < string , Func < string , CultureInfo , object > > numberSuffixToParse = new Dictionary < string , Func < string , CultureInfo , object > > ( StringComparer . OrdinalIgnoreCase ) // Always Case insensitive, like in C#
167
170
{
168
- { "f" , number => float . Parse ( number , NumberStyles . Any , CultureInfo . InvariantCulture ) } ,
169
- { "d" , number => double . Parse ( number , NumberStyles . Any , CultureInfo . InvariantCulture ) } ,
170
- { "u" , number => uint . Parse ( number , NumberStyles . Any , CultureInfo . InvariantCulture ) } ,
171
- { "l" , number => long . Parse ( number , NumberStyles . Any , CultureInfo . InvariantCulture ) } ,
172
- { "ul" , number => ulong . Parse ( number , NumberStyles . Any , CultureInfo . InvariantCulture ) } ,
173
- { "m" , number => decimal . Parse ( number , NumberStyles . Any , CultureInfo . InvariantCulture ) }
171
+ { "f" , ( number , culture ) => float . Parse ( number , NumberStyles . Any , culture ) } ,
172
+ { "d" , ( number , culture ) => double . Parse ( number , NumberStyles . Any , culture ) } ,
173
+ { "u" , ( number , culture ) => uint . Parse ( number , NumberStyles . Any , culture ) } ,
174
+ { "l" , ( number , culture ) => long . Parse ( number , NumberStyles . Any , culture ) } ,
175
+ { "ul" , ( number , culture ) => ulong . Parse ( number , NumberStyles . Any , culture ) } ,
176
+ { "m" , ( number , culture ) => decimal . Parse ( number , NumberStyles . Any , culture ) }
174
177
} ;
175
178
176
179
private static Dictionary < char , string > stringEscapedCharDict = new Dictionary < char , string > ( )
@@ -529,6 +532,42 @@ private StringComparer StringComparerForCasing
529
532
}
530
533
}
531
534
535
+ private CultureInfo cultureInfoForNumberParsing = CultureInfo . InvariantCulture . Clone ( ) as CultureInfo ;
536
+ private string optionNumberParsingDecimalSeparator = "." ;
537
+
538
+ /// <summary>
539
+ /// Allow to change the decimal separator of numbers when parsing expressions.
540
+ /// By default "."
541
+ /// Warning if using comma change also OptionFunctionArgumentsSeparator and OptionInitializersSeparator otherwise it will create conflicts
542
+ /// </summary>
543
+ public string OptionNumberParsingDecimalSeparator
544
+ {
545
+
546
+ get => optionNumberParsingDecimalSeparator ;
547
+
548
+ set
549
+ {
550
+ optionNumberParsingDecimalSeparator = value ;
551
+ cultureInfoForNumberParsing . NumberFormat . NumberDecimalSeparator = value ;
552
+
553
+ numberRegex = new Regex ( string . Format ( numberRegexPattern , Regex . Escape ( optionNumberParsingDecimalSeparator ) ) , RegexOptions . IgnoreCase ) ;
554
+ }
555
+ }
556
+
557
+ /// <summary>
558
+ /// Allow to change the separator of functions arguments.
559
+ /// By default ","
560
+ /// Warning must to be changed if OptionNumberParsingDecimalSeparator = "," otherwise it will create conflicts
561
+ /// </summary>
562
+ public string OptionFunctionArgumentsSeparator { get ; set ; } = "," ;
563
+
564
+ /// <summary>
565
+ /// Allow to change the separator of Object and collections Initialization between { and } after the keyword new.
566
+ /// By default ","
567
+ /// Warning must to be changed if OptionNumberParsingDecimalSeparator = "," otherwise it will create conflicts
568
+ /// </summary>
569
+ public string OptionInitializersSeparator { get ; set ; } = "," ;
570
+
532
571
/// <summary>
533
572
/// if <c>true</c> allow to add the prefix Fluid or Fluent before void methods names to return back the instance on which the method is call.
534
573
/// if <c>false</c> unactive this functionality.
@@ -746,6 +785,7 @@ public ExpressionEvaluator()
746
785
{
747
786
Assemblies . AddRange ( AppDomain . CurrentDomain . GetAssemblies ( ) ) ;
748
787
instanceCreationWithNewKeywordRegex = new Regex ( InstanceCreationWithNewKeywordRegexPattern ) ;
788
+ numberRegex = new Regex ( string . Format ( numberRegexPattern , @"\." ) , RegexOptions . IgnoreCase ) ;
749
789
castRegex = new Regex ( CastRegexPattern ) ;
750
790
}
751
791
@@ -1419,20 +1459,20 @@ private bool EvaluateNumber(string restOfExpression, Stack<object> stack, ref in
1419
1459
string type = numberMatch . Groups [ "type" ] . Value ;
1420
1460
string numberNoType = numberMatch . Value . Replace ( type , string . Empty ) . Replace ( "_" , "" ) ;
1421
1461
1422
- if ( numberSuffixToParse . TryGetValue ( type , out Func < string , object > parseFunc ) )
1462
+ if ( numberSuffixToParse . TryGetValue ( type , out Func < string , CultureInfo , object > parseFunc ) )
1423
1463
{
1424
- stack . Push ( parseFunc ( numberNoType ) ) ;
1464
+ stack . Push ( parseFunc ( numberNoType , cultureInfoForNumberParsing ) ) ;
1425
1465
}
1426
1466
}
1427
1467
else
1428
1468
{
1429
1469
if ( numberMatch . Groups [ "hasdecimal" ] . Success )
1430
1470
{
1431
- stack . Push ( double . Parse ( numberMatch . Value . Replace ( "_" , "" ) , NumberStyles . Any , CultureInfo . InvariantCulture ) ) ;
1471
+ stack . Push ( double . Parse ( numberMatch . Value . Replace ( "_" , "" ) , NumberStyles . Any , cultureInfoForNumberParsing ) ) ;
1432
1472
}
1433
1473
else
1434
1474
{
1435
- stack . Push ( int . Parse ( numberMatch . Value . Replace ( "_" , "" ) , NumberStyles . Any , CultureInfo . InvariantCulture ) ) ;
1475
+ stack . Push ( int . Parse ( numberMatch . Value . Replace ( "_" , "" ) , NumberStyles . Any , cultureInfoForNumberParsing ) ) ;
1436
1476
}
1437
1477
}
1438
1478
@@ -1478,7 +1518,7 @@ void Init(object element, List<string> initArgs)
1478
1518
{
1479
1519
int subIndex = subExpr . IndexOf ( "{" ) + 1 ;
1480
1520
1481
- List < string > subArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( subExpr , ref subIndex , true , "," , "{" , "}" ) ;
1521
+ List < string > subArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( subExpr , ref subIndex , true , OptionInitializersSeparator , "{" , "}" ) ;
1482
1522
1483
1523
if ( subArgs . Count == 2 )
1484
1524
{
@@ -1514,7 +1554,7 @@ void Init(object element, List<string> initArgs)
1514
1554
1515
1555
if ( instanceCreationMatch . Groups [ "isfunction" ] . Success )
1516
1556
{
1517
- List < string > constructorArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true ) ;
1557
+ List < string > constructorArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , OptionFunctionArgumentsSeparator ) ;
1518
1558
i ++ ;
1519
1559
1520
1560
List < object > cArgs = constructorArgs . ConvertAll ( arg => Evaluate ( arg ) ) ;
@@ -1527,7 +1567,7 @@ void Init(object element, List<string> initArgs)
1527
1567
{
1528
1568
i += blockBeginningMatch . Length ;
1529
1569
1530
- List < string > initArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , "," , "{" , "}" ) ;
1570
+ List < string > initArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , OptionInitializersSeparator , "{" , "}" ) ;
1531
1571
1532
1572
Init ( element , initArgs ) ;
1533
1573
}
@@ -1540,15 +1580,15 @@ void Init(object element, List<string> initArgs)
1540
1580
{
1541
1581
object element = Activator . CreateInstance ( type , new object [ 0 ] ) ;
1542
1582
1543
- List < string > initArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , "," , "{" , "}" ) ;
1583
+ List < string > initArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , OptionInitializersSeparator , "{" , "}" ) ;
1544
1584
1545
1585
Init ( element , initArgs ) ;
1546
1586
1547
1587
stack . Push ( element ) ;
1548
1588
}
1549
1589
else if ( instanceCreationMatch . Groups [ "isArray" ] . Success )
1550
1590
{
1551
- List < string > arrayArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , "," , "[" , "]" ) ;
1591
+ List < string > arrayArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , OptionInitializersSeparator , "[" , "]" ) ;
1552
1592
i ++ ;
1553
1593
Array array = null ;
1554
1594
@@ -1563,7 +1603,7 @@ void Init(object element, List<string> initArgs)
1563
1603
{
1564
1604
i += initInNewBeginningMatch . Length ;
1565
1605
1566
- List < string > arrayElements = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , "," , "{" , "}" ) ;
1606
+ List < string > arrayElements = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , OptionInitializersSeparator , "{" , "}" ) ;
1567
1607
i ++ ;
1568
1608
1569
1609
if ( array == null )
@@ -1602,7 +1642,7 @@ private bool EvaluateVarOrFunc(string expr, string restOfExpression, Stack<objec
1602
1642
1603
1643
if ( varFuncMatch . Groups [ "isfunction" ] . Success )
1604
1644
{
1605
- List < string > funcArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true ) ;
1645
+ List < string > funcArgs = GetExpressionsBetweenParenthesesOrOtherImbricableBrackets ( expr , ref i , true , OptionFunctionArgumentsSeparator ) ;
1606
1646
if ( varFuncMatch . Groups [ "inObject" ] . Success )
1607
1647
{
1608
1648
if ( stack . Count == 0 || stack . Peek ( ) is ExpressionOperator )
0 commit comments