Skip to content

Commit bd3fe7c

Browse files
authored
Fix NumberParsing for double with exponent (#532)
* Fix NumberParsing dor double with exponent
1 parent 7d59802 commit bd3fe7c

File tree

6 files changed

+281
-135
lines changed

6 files changed

+281
-135
lines changed

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

+30-80
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class ExpressionParser
2828
private readonly MethodFinder _methodFinder;
2929
private readonly IKeywordsHelper _keywordsHelper;
3030
private readonly TextParser _textParser;
31+
private readonly NumberParser _numberParser;
3132
private readonly IExpressionHelper _expressionHelper;
3233
private readonly ITypeFinder _typeFinder;
3334
private readonly ITypeConverterFactory _typeConverterFactory;
@@ -83,6 +84,7 @@ public ExpressionParser([CanBeNull] ParameterExpression[] parameters, [NotNull]
8384

8485
_keywordsHelper = new KeywordsHelper(_parsingConfig);
8586
_textParser = new TextParser(_parsingConfig, expression);
87+
_numberParser = new NumberParser(parsingConfig);
8688
_methodFinder = new MethodFinder(_parsingConfig);
8789
_expressionHelper = new ExpressionHelper(_parsingConfig);
8890
_typeFinder = new TypeFinder(_parsingConfig, _keywordsHelper);
@@ -876,16 +878,14 @@ Expression ParseIntegerLiteral()
876878
if (!string.IsNullOrEmpty(qualifier))
877879
{
878880
if (qualifier == "L" || qualifier == "l")
881+
{
879882
return ConstantExpressionHelper.CreateLiteral(value, text);
883+
}
880884

881-
if (qualifier == "F" || qualifier == "f")
882-
return TryParseAsFloat(text, qualifier[0]);
883-
884-
if (qualifier == "D" || qualifier == "d")
885-
return TryParseAsDouble(text, qualifier[0]);
886-
887-
if (qualifier == "M" || qualifier == "m")
888-
return TryParseAsDecimal(text, qualifier[0]);
885+
if (qualifier == "F" || qualifier == "f" || qualifier == "D" || qualifier == "d" || qualifier == "M" || qualifier == "m")
886+
{
887+
return ParseRealLiteral(text, qualifier[0], false);
888+
}
889889

890890
throw ParseError(Res.MinusCannotBeAppliedToUnsignedInteger);
891891
}
@@ -904,54 +904,40 @@ Expression ParseRealLiteral()
904904
_textParser.ValidateToken(TokenId.RealLiteral);
905905

906906
string text = _textParser.CurrentToken.Text;
907-
char qualifier = text[text.Length - 1];
908907

909908
_textParser.NextToken();
910-
return TryParseAsFloat(text, qualifier);
911-
}
912-
913-
Expression TryParseAsFloat(string text, char qualifier)
914-
{
915-
if (qualifier == 'F' || qualifier == 'f')
916-
{
917-
if (float.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Float, _parsingConfig.NumberParseCulture, out float f))
918-
{
919-
return ConstantExpressionHelper.CreateLiteral(f, text);
920-
}
921-
}
922909

923-
// not possible to find float qualifier, so try to parse as double
924-
return TryParseAsDecimal(text, qualifier);
910+
return ParseRealLiteral(text, text[text.Length - 1], true);
925911
}
926912

927-
Expression TryParseAsDecimal(string text, char qualifier)
913+
Expression ParseRealLiteral(string text, char qualifier, bool stripQualifier)
928914
{
929-
if (qualifier == 'M' || qualifier == 'm')
915+
object o;
916+
switch (qualifier)
930917
{
931-
if (decimal.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number, _parsingConfig.NumberParseCulture, out decimal d))
932-
{
933-
return ConstantExpressionHelper.CreateLiteral(d, text);
934-
}
935-
}
918+
case 'f':
919+
case 'F':
920+
o = _numberParser.ParseNumber(stripQualifier ? text.Substring(0, text.Length - 1) : text, typeof(float));
921+
break;
936922

937-
// not possible to find float qualifier, so try to parse as double
938-
return TryParseAsDouble(text, qualifier);
939-
}
923+
case 'm':
924+
case 'M':
925+
o = _numberParser.ParseNumber(stripQualifier ? text.Substring(0, text.Length - 1) : text, typeof(decimal));
926+
break;
940927

941-
Expression TryParseAsDouble(string text, char qualifier)
942-
{
943-
double d;
944-
if (qualifier == 'D' || qualifier == 'd')
945-
{
946-
if (double.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number, _parsingConfig.NumberParseCulture, out d))
947-
{
948-
return ConstantExpressionHelper.CreateLiteral(d, text);
949-
}
928+
case 'd':
929+
case 'D':
930+
o = _numberParser.ParseNumber(stripQualifier ? text.Substring(0, text.Length - 1) : text, typeof(double));
931+
break;
932+
933+
default:
934+
o = _numberParser.ParseNumber(text, typeof(double));
935+
break;
950936
}
951937

952-
if (double.TryParse(text, NumberStyles.Number, _parsingConfig.NumberParseCulture, out d))
938+
if (o != null)
953939
{
954-
return ConstantExpressionHelper.CreateLiteral(d, text);
940+
return ConstantExpressionHelper.CreateLiteral(o, text);
955941
}
956942

957943
throw ParseError(Res.InvalidRealLiteral, text);
@@ -1045,42 +1031,6 @@ Expression ParseIdentifier()
10451031
return expr;
10461032
}
10471033

1048-
//// This could be enum like "MyEnum.Value1"
1049-
//if (_textParser.CurrentToken.Id == TokenId.Identifier)
1050-
//{
1051-
// var parts = new List<string> { _textParser.CurrentToken.Text };
1052-
1053-
// _textParser.NextToken();
1054-
// _textParser.ValidateToken(TokenId.Dot, Res.DotExpected);
1055-
// while (_textParser.CurrentToken.Id == TokenId.Dot || _textParser.CurrentToken.Id == TokenId.Plus)
1056-
// {
1057-
// parts.Add(_textParser.CurrentToken.Text);
1058-
1059-
// _textParser.NextToken();
1060-
// _textParser.ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
1061-
1062-
// parts.Add(_textParser.CurrentToken.Text);
1063-
1064-
// _textParser.NextToken();
1065-
// }
1066-
1067-
// var enumTypeAsString = string.Join("", parts.Take(parts.Count - 2).ToArray());
1068-
// var enumType = _typeFinder.FindTypeByName(enumTypeAsString, null, true);
1069-
// if (enumType == null)
1070-
// {
1071-
// throw ParseError(_textParser.CurrentToken.Pos, Res.EnumTypeNotFound, enumTypeAsString);
1072-
// }
1073-
1074-
// string enumValue = parts.Last();
1075-
// var @enum = TypeHelper.ParseEnum(enumValue, enumType);
1076-
// if (@enum == null)
1077-
// {
1078-
// throw ParseError(_textParser.CurrentToken.Pos, Res.EnumValueNotDefined, enumValue, enumTypeAsString);
1079-
// }
1080-
1081-
// return Expression.Constant(@enum);
1082-
//}
1083-
10841034
if (_it != null)
10851035
{
10861036
return ParseMemberAccess(null, _it);

src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ public virtual Expression Promote(Expression expr, Type type, bool exact, bool c
6060
break;
6161

6262
case TypeCode.Double:
63-
if (target == typeof(decimal) || target == typeof(double)) value = _numberParser.ParseNumber(text, target);
63+
if (target == typeof(decimal) || target == typeof(double))
64+
{
65+
value = _numberParser.ParseNumber(text, target);
66+
}
6467
break;
6568

6669
case TypeCode.String:

src/System.Linq.Dynamic.Core/Parser/NumberParser.cs

+41-26
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1-
namespace System.Linq.Dynamic.Core.Parser
1+
using System.Globalization;
2+
using JetBrains.Annotations;
3+
4+
namespace System.Linq.Dynamic.Core.Parser
25
{
36
/// <summary>
47
/// NumberParser
58
/// </summary>
69
public class NumberParser
710
{
8-
private readonly ParsingConfig _config;
11+
private readonly CultureInfo _culture;
912

1013
/// <summary>
1114
/// Initializes a new instance of the <see cref="NumberParser"/> class.
1215
/// </summary>
1316
/// <param name="config">The ParsingConfig.</param>
14-
public NumberParser(ParsingConfig config)
17+
public NumberParser([CanBeNull] ParsingConfig config)
18+
{
19+
_culture = config?.NumberParseCulture ?? CultureInfo.InvariantCulture;
20+
}
21+
22+
/// <summary>
23+
/// Tries to parse the number (text) into the specified type.
24+
/// </summary>
25+
/// <param name="text">The text.</param>
26+
/// <param name="type">The type.</param>
27+
/// <param name="result">The result.</param>
28+
public bool TryParseNumber(string text, Type type, out object result)
1529
{
16-
_config = config;
30+
result = ParseNumber(text, type);
31+
return type != null;
1732
}
1833

1934
/// <summary>
@@ -29,73 +44,73 @@ public object ParseNumber(string text, Type type)
2944
switch (Type.GetTypeCode(TypeHelper.GetNonNullableType(type)))
3045
{
3146
case TypeCode.SByte:
32-
return sbyte.Parse(text, _config.NumberParseCulture);
47+
return sbyte.Parse(text, _culture);
3348
case TypeCode.Byte:
34-
return byte.Parse(text, _config.NumberParseCulture);
49+
return byte.Parse(text, _culture);
3550
case TypeCode.Int16:
36-
return short.Parse(text, _config.NumberParseCulture);
51+
return short.Parse(text, _culture);
3752
case TypeCode.UInt16:
38-
return ushort.Parse(text, _config.NumberParseCulture);
53+
return ushort.Parse(text, _culture);
3954
case TypeCode.Int32:
40-
return int.Parse(text, _config.NumberParseCulture);
55+
return int.Parse(text, _culture);
4156
case TypeCode.UInt32:
42-
return uint.Parse(text, _config.NumberParseCulture);
57+
return uint.Parse(text, _culture);
4358
case TypeCode.Int64:
44-
return long.Parse(text, _config.NumberParseCulture);
59+
return long.Parse(text, _culture);
4560
case TypeCode.UInt64:
46-
return ulong.Parse(text, _config.NumberParseCulture);
61+
return ulong.Parse(text, _culture);
4762
case TypeCode.Single:
48-
return float.Parse(text, _config.NumberParseCulture);
63+
return float.Parse(text, _culture);
4964
case TypeCode.Double:
50-
return double.Parse(text, _config.NumberParseCulture);
65+
return double.Parse(text, _culture);
5166
case TypeCode.Decimal:
52-
return decimal.Parse(text, _config.NumberParseCulture);
67+
return decimal.Parse(text, _culture);
5368
}
5469
#else
5570
var tp = TypeHelper.GetNonNullableType(type);
5671
if (tp == typeof(sbyte))
5772
{
58-
return sbyte.Parse(text, _config.NumberParseCulture);
73+
return sbyte.Parse(text, _culture);
5974
}
6075
if (tp == typeof(byte))
6176
{
62-
return byte.Parse(text, _config.NumberParseCulture);
77+
return byte.Parse(text, _culture);
6378
}
6479
if (tp == typeof(short))
6580
{
66-
return short.Parse(text, _config.NumberParseCulture);
81+
return short.Parse(text, _culture);
6782
}
6883
if (tp == typeof(ushort))
6984
{
70-
return ushort.Parse(text, _config.NumberParseCulture);
85+
return ushort.Parse(text, _culture);
7186
}
7287
if (tp == typeof(int))
7388
{
74-
return int.Parse(text, _config.NumberParseCulture);
89+
return int.Parse(text, _culture);
7590
}
7691
if (tp == typeof(uint))
7792
{
78-
return uint.Parse(text, _config.NumberParseCulture);
93+
return uint.Parse(text, _culture);
7994
}
8095
if (tp == typeof(long))
8196
{
82-
return long.Parse(text, _config.NumberParseCulture);
97+
return long.Parse(text, _culture);
8398
}
8499
if (tp == typeof(ulong))
85100
{
86-
return ulong.Parse(text, _config.NumberParseCulture);
101+
return ulong.Parse(text, _culture);
87102
}
88103
if (tp == typeof(float))
89104
{
90-
return float.Parse(text, _config.NumberParseCulture);
105+
return float.Parse(text, _culture);
91106
}
92107
if (tp == typeof(double))
93108
{
94-
return double.Parse(text, _config.NumberParseCulture);
109+
return double.Parse(text, _culture);
95110
}
96111
if (tp == typeof(decimal))
97112
{
98-
return decimal.Parse(text, _config.NumberParseCulture);
113+
return decimal.Parse(text, _culture);
99114
}
100115
#endif
101116
}

0 commit comments

Comments
 (0)