diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index 4cc77a00..b0fb8157 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -984,11 +984,12 @@ private Expression ParseRealLiteral()
{
_textParser.ValidateToken(TokenId.RealLiteral);
- string text = _textParser.CurrentToken.Text;
+ var text = _textParser.CurrentToken.Text;
+ var textOriginal = text;
_textParser.NextToken();
- return _numberParser.ParseRealLiteral(text, text[text.Length - 1], true);
+ return _numberParser.ParseRealLiteral(text, textOriginal, text[text.Length - 1], true);
}
private Expression ParseParenExpression()
diff --git a/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs b/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs
index 7fdfdaad..798ff097 100644
--- a/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs
@@ -4,244 +4,234 @@
using System.Linq.Expressions;
using System.Text.RegularExpressions;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+///
+/// NumberParser
+///
+public class NumberParser
{
+ private static readonly Regex RegexBinary32 = new("^[01]{1,32}$", RegexOptions.Compiled);
+ private static readonly Regex RegexBinary64 = new("^[01]{1,64}$", RegexOptions.Compiled);
+ private static readonly char[] Qualifiers = { 'U', 'u', 'L', 'l', 'F', 'f', 'D', 'd', 'M', 'm' };
+ private static readonly char[] QualifiersHex = { 'U', 'u', 'L', 'l' };
+ private static readonly string[] QualifiersReal = { "F", "f", "D", "d", "M", "m" };
+ private readonly ConstantExpressionHelper _constantExpressionHelper;
+
+ private readonly CultureInfo _culture;
+
///
- /// NumberParser
+ /// Initializes a new instance of the class.
///
- public class NumberParser
+ /// The ParsingConfig.
+ public NumberParser(ParsingConfig? config)
{
- private static readonly Regex RegexBinary32 = new("^[01]{1,32}$", RegexOptions.Compiled);
- private static readonly Regex RegexBinary64 = new("^[01]{1,64}$", RegexOptions.Compiled);
- private static readonly char[] Qualifiers = { 'U', 'u', 'L', 'l', 'F', 'f', 'D', 'd', 'M', 'm' };
- private static readonly char[] QualifiersHex = { 'U', 'u', 'L', 'l' };
- private static readonly string[] QualifiersReal = { "F", "f", "D", "d", "M", "m" };
- private readonly ConstantExpressionHelper _constantExpressionHelper;
-
- private readonly CultureInfo _culture;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The ParsingConfig.
- public NumberParser(ParsingConfig? config)
- {
- _culture = config?.NumberParseCulture ?? CultureInfo.InvariantCulture;
- _constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(config ?? ParsingConfig.Default);
- }
+ _culture = config?.NumberParseCulture ?? CultureInfo.InvariantCulture;
+ _constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(config ?? ParsingConfig.Default);
+ }
- ///
- /// Tries to parse the text into a IntegerLiteral ConstantExpression.
- ///
- /// The current token position (needed for error reporting).
- /// The text.
- public Expression ParseIntegerLiteral(int tokenPosition, string text)
- {
- Check.NotEmpty(text, nameof(text));
+ ///
+ /// Tries to parse the text into a IntegerLiteral ConstantExpression.
+ ///
+ /// The current token position (needed for error reporting).
+ /// The text.
+ public Expression ParseIntegerLiteral(int tokenPosition, string text)
+ {
+ Check.NotEmpty(text);
- var last = text[text.Length - 1];
- var isNegative = text[0] == '-';
- var isHexadecimal = text.StartsWith(isNegative ? "-0x" : "0x", StringComparison.OrdinalIgnoreCase);
- var isBinary = text.StartsWith(isNegative ? "-0b" : "0b", StringComparison.OrdinalIgnoreCase);
- var qualifiers = isHexadecimal ? QualifiersHex : Qualifiers;
+ var textOriginal = text;
+ var last = text[text.Length - 1];
+ var isNegative = text[0] == '-';
+ var isHexadecimal = text.StartsWith(isNegative ? "-0x" : "0x", StringComparison.OrdinalIgnoreCase);
+ var isBinary = text.StartsWith(isNegative ? "-0b" : "0b", StringComparison.OrdinalIgnoreCase);
+ var qualifiers = isHexadecimal ? QualifiersHex : Qualifiers;
- string? qualifier = null;
- if (qualifiers.Contains(last))
+ string? qualifier = null;
+ if (qualifiers.Contains(last))
+ {
+ int pos = text.Length - 1, count = 0;
+ while (qualifiers.Contains(text[pos]))
{
- int pos = text.Length - 1, count = 0;
- while (qualifiers.Contains(text[pos]))
- {
- ++count;
- --pos;
- }
- qualifier = text.Substring(text.Length - count, count);
- text = text.Substring(0, text.Length - count);
+ ++count;
+ --pos;
}
+ qualifier = text.Substring(text.Length - count, count);
+ text = text.Substring(0, text.Length - count);
+ }
- if (!isNegative)
+ if (!isNegative)
+ {
+ if (isHexadecimal || isBinary)
{
- if (isHexadecimal || isBinary)
- {
- text = text.Substring(2);
- }
-
- if (isBinary)
- {
- return ParseAsBinary(tokenPosition, text, isNegative);
- }
+ text = text.Substring(2);
+ }
- if (!ulong.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _culture, out ulong unsignedValue))
- {
- throw new ParseException(string.Format(_culture, Res.InvalidIntegerLiteral, text), tokenPosition);
- }
+ if (isBinary)
+ {
+ return ParseAsBinary(tokenPosition, text, textOriginal, isNegative);
+ }
- if (!string.IsNullOrEmpty(qualifier) && qualifier!.Length > 0)
- {
- if (qualifier == "U" || qualifier == "u")
- {
- return _constantExpressionHelper.CreateLiteral((uint)unsignedValue, text);
- }
-
- if (qualifier == "L" || qualifier == "l")
- {
- return _constantExpressionHelper.CreateLiteral((long)unsignedValue, text);
- }
-
- if (QualifiersReal.Contains(qualifier))
- {
- return ParseRealLiteral(text, qualifier[0], false);
- }
-
- return _constantExpressionHelper.CreateLiteral(unsignedValue, text);
- }
+ if (!ulong.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _culture, out ulong unsignedValue))
+ {
+ throw new ParseException(string.Format(_culture, Res.InvalidIntegerLiteral, text), tokenPosition);
+ }
- if (unsignedValue <= int.MaxValue)
+ if (!string.IsNullOrEmpty(qualifier) && qualifier!.Length > 0)
+ {
+ if (qualifier == "U" || qualifier == "u")
{
- return _constantExpressionHelper.CreateLiteral((int)unsignedValue, text);
+ return _constantExpressionHelper.CreateLiteral((uint)unsignedValue, textOriginal);
}
- if (unsignedValue <= uint.MaxValue)
+ if (qualifier == "L" || qualifier == "l")
{
- return _constantExpressionHelper.CreateLiteral((uint)unsignedValue, text);
+ return _constantExpressionHelper.CreateLiteral((long)unsignedValue, textOriginal);
}
- if (unsignedValue <= long.MaxValue)
+ if (QualifiersReal.Contains(qualifier))
{
- return _constantExpressionHelper.CreateLiteral((long)unsignedValue, text);
+ return ParseRealLiteral(text, textOriginal, qualifier[0], false);
}
- return _constantExpressionHelper.CreateLiteral(unsignedValue, text);
+ return _constantExpressionHelper.CreateLiteral(unsignedValue, textOriginal);
}
- if (isHexadecimal || isBinary)
+ if (unsignedValue <= int.MaxValue)
{
- text = text.Substring(3);
+ return _constantExpressionHelper.CreateLiteral((int)unsignedValue, textOriginal);
}
- if (isBinary)
+ if (unsignedValue <= uint.MaxValue)
{
- return ParseAsBinary(tokenPosition, text, isNegative);
+ return _constantExpressionHelper.CreateLiteral((uint)unsignedValue, textOriginal);
}
- if (!long.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _culture, out long value))
+ if (unsignedValue <= long.MaxValue)
{
- throw new ParseException(string.Format(_culture, Res.InvalidIntegerLiteral, text), tokenPosition);
+ return _constantExpressionHelper.CreateLiteral((long)unsignedValue, textOriginal);
}
- if (isHexadecimal)
- {
- value = -value;
- }
+ return _constantExpressionHelper.CreateLiteral(unsignedValue, textOriginal);
+ }
- if (!string.IsNullOrEmpty(qualifier) && qualifier!.Length > 0)
- {
- if (qualifier == "L" || qualifier == "l")
- {
- return _constantExpressionHelper.CreateLiteral(value, text);
- }
+ if (isHexadecimal || isBinary)
+ {
+ text = text.Substring(3);
+ }
- if (QualifiersReal.Contains(qualifier))
- {
- return ParseRealLiteral(text, qualifier[0], false);
- }
+ if (isBinary)
+ {
+ return ParseAsBinary(tokenPosition, text, textOriginal, isNegative);
+ }
- throw new ParseException(Res.MinusCannotBeAppliedToUnsignedInteger, tokenPosition);
+ if (!long.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _culture, out long value))
+ {
+ throw new ParseException(string.Format(_culture, Res.InvalidIntegerLiteral, text), tokenPosition);
+ }
+
+ if (isHexadecimal)
+ {
+ value = -value;
+ }
+
+ if (!string.IsNullOrEmpty(qualifier) && qualifier!.Length > 0)
+ {
+ if (qualifier == "L" || qualifier == "l")
+ {
+ return _constantExpressionHelper.CreateLiteral(value, textOriginal);
}
- if (value <= int.MaxValue)
+ if (QualifiersReal.Contains(qualifier))
{
- return _constantExpressionHelper.CreateLiteral((int)value, text);
+ return ParseRealLiteral(text, textOriginal, qualifier[0], false);
}
- return _constantExpressionHelper.CreateLiteral(value, text);
+ throw new ParseException(Res.MinusCannotBeAppliedToUnsignedInteger, tokenPosition);
}
- ///
- /// Parse the text into a Real ConstantExpression.
- ///
- public Expression ParseRealLiteral(string text, char qualifier, bool stripQualifier)
+ if (value <= int.MaxValue)
{
- if (stripQualifier)
- {
- var pos = text.Length - 1;
- while (pos >= 0 && Qualifiers.Contains(text[pos]))
- {
- pos--;
- }
+ return _constantExpressionHelper.CreateLiteral((int)value, textOriginal);
+ }
- if (pos < text.Length - 1)
- {
- qualifier = text[pos + 1];
- text = text.Substring(0, pos + 1);
- }
- }
+ return _constantExpressionHelper.CreateLiteral(value, textOriginal);
+ }
- switch (qualifier)
+ ///
+ /// Parse the text into a Real ConstantExpression.
+ ///
+ public Expression ParseRealLiteral(string text, string textOriginal, char qualifier, bool stripQualifier)
+ {
+ if (stripQualifier)
+ {
+ var pos = text.Length - 1;
+ while (pos >= 0 && Qualifiers.Contains(text[pos]))
{
- case 'f':
- case 'F':
- return _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(float))!, text);
-
- case 'm':
- case 'M':
- return _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(decimal))!, text);
-
- case 'd':
- case 'D':
- return _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(double))!, text);
+ pos--;
+ }
- default:
- return _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(double))!, text);
+ if (pos < text.Length - 1)
+ {
+ qualifier = text[pos + 1];
+ text = text.Substring(0, pos + 1);
}
}
- ///
- /// Tries to parse the number (text) into the specified type.
- ///
- /// The text.
- /// The type.
- /// The result.
- public bool TryParseNumber(string text, Type type, out object? result)
+ return qualifier switch
{
- result = ParseNumber(text, type);
- return result != null;
- }
+ 'f' or 'F' => _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(float))!, textOriginal),
+ 'm' or 'M' => _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(decimal))!, textOriginal),
+ _ => _constantExpressionHelper.CreateLiteral(ParseNumber(text, typeof(double))!, textOriginal)
+ };
+ }
- ///
- /// Parses the number (text) into the specified type.
- ///
- /// The text.
- /// The type.
- public object? ParseNumber(string text, Type type)
+ ///
+ /// Tries to parse the number (text) into the specified type.
+ ///
+ /// The text.
+ /// The type.
+ /// The result.
+ public bool TryParseNumber(string text, Type type, out object? result)
+ {
+ result = ParseNumber(text, type);
+ return result != null;
+ }
+
+ ///
+ /// Parses the number (text) into the specified type.
+ ///
+ /// The text.
+ /// The type.
+ public object? ParseNumber(string text, Type type)
+ {
+ try
{
- try
- {
#if !(UAP10_0 || NETSTANDARD)
- switch (Type.GetTypeCode(TypeHelper.GetNonNullableType(type)))
- {
- case TypeCode.SByte:
- return sbyte.Parse(text, _culture);
- case TypeCode.Byte:
- return byte.Parse(text, _culture);
- case TypeCode.Int16:
- return short.Parse(text, _culture);
- case TypeCode.UInt16:
- return ushort.Parse(text, _culture);
- case TypeCode.Int32:
- return int.Parse(text, _culture);
- case TypeCode.UInt32:
- return uint.Parse(text, _culture);
- case TypeCode.Int64:
- return long.Parse(text, _culture);
- case TypeCode.UInt64:
- return ulong.Parse(text, _culture);
- case TypeCode.Single:
- return float.Parse(text, _culture);
- case TypeCode.Double:
- return double.Parse(text, _culture);
- case TypeCode.Decimal:
- return decimal.Parse(text, _culture);
- }
+ switch (Type.GetTypeCode(TypeHelper.GetNonNullableType(type)))
+ {
+ case TypeCode.SByte:
+ return sbyte.Parse(text, _culture);
+ case TypeCode.Byte:
+ return byte.Parse(text, _culture);
+ case TypeCode.Int16:
+ return short.Parse(text, _culture);
+ case TypeCode.UInt16:
+ return ushort.Parse(text, _culture);
+ case TypeCode.Int32:
+ return int.Parse(text, _culture);
+ case TypeCode.UInt32:
+ return uint.Parse(text, _culture);
+ case TypeCode.Int64:
+ return long.Parse(text, _culture);
+ case TypeCode.UInt64:
+ return ulong.Parse(text, _culture);
+ case TypeCode.Single:
+ return float.Parse(text, _culture);
+ case TypeCode.Double:
+ return double.Parse(text, _culture);
+ case TypeCode.Decimal:
+ return decimal.Parse(text, _culture);
+ }
#else
var tp = TypeHelper.GetNonNullableType(type);
if (tp == typeof(sbyte))
@@ -289,28 +279,27 @@ public bool TryParseNumber(string text, Type type, out object? result)
return decimal.Parse(text, _culture);
}
#endif
- }
- catch
- {
- return null;
- }
-
+ }
+ catch
+ {
return null;
}
- private Expression ParseAsBinary(int tokenPosition, string text, bool isNegative)
- {
- if (RegexBinary32.IsMatch(text))
- {
- return _constantExpressionHelper.CreateLiteral((isNegative ? -1 : 1) * Convert.ToInt32(text, 2), text);
- }
+ return null;
+ }
- if (RegexBinary64.IsMatch(text))
- {
- return _constantExpressionHelper.CreateLiteral((isNegative ? -1 : 1) * Convert.ToInt64(text, 2), text);
- }
+ private Expression ParseAsBinary(int tokenPosition, string text, string textOriginal, bool isNegative)
+ {
+ if (RegexBinary32.IsMatch(text))
+ {
+ return _constantExpressionHelper.CreateLiteral((isNegative ? -1 : 1) * Convert.ToInt32(text, 2), textOriginal);
+ }
- throw new ParseException(string.Format(_culture, Res.InvalidBinaryIntegerLiteral, text), tokenPosition);
+ if (RegexBinary64.IsMatch(text))
+ {
+ return _constantExpressionHelper.CreateLiteral((isNegative ? -1 : 1) * Convert.ToInt64(text, 2), textOriginal);
}
+
+ throw new ParseException(string.Format(_culture, Res.InvalidBinaryIntegerLiteral, text), tokenPosition);
}
-}
+}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
index 4d5d19b5..70d5ce68 100644
--- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
@@ -924,6 +924,7 @@ public void DynamicExpressionParser_ParseLambda_Float(string? culture, string ex
[null, "1.2345E4", 12345d]
];
}
+
[Theory]
[MemberData(nameof(Doubles))]
public void DynamicExpressionParser_ParseLambda_Double(string? culture, string expression, double expected)
@@ -945,6 +946,42 @@ public void DynamicExpressionParser_ParseLambda_Double(string? culture, string e
result.Should().Be(expected);
}
+ [Theory]
+ [InlineData("0x0", 0)]
+ [InlineData("0xa", 10)]
+ [InlineData("0xA", 10)]
+ [InlineData("0x10", 16)]
+ public void DynamicExpressionParser_ParseLambda_HexToLong(string expression, long expected)
+ {
+ // Arrange
+ var parameters = Array.Empty();
+
+ // Act
+ var lambda = DynamicExpressionParser.ParseLambda( parameters, typeof(long), expression);
+ var result = lambda.Compile().DynamicInvoke();
+
+ // Assert
+ result.Should().Be(expected);
+ }
+
+ [Theory]
+ [InlineData("0b0", 0)]
+ [InlineData("0B0", 0)]
+ [InlineData("0b1000", 8)]
+ [InlineData("0b1001", 9)]
+ public void DynamicExpressionParser_ParseLambda_BinaryToLong(string expression, long expected)
+ {
+ // Arrange
+ var parameters = Array.Empty();
+
+ // Act
+ var lambda = DynamicExpressionParser.ParseLambda(parameters, typeof(long), expression);
+ var result = lambda.Compile().DynamicInvoke();
+
+ // Assert
+ result.Should().Be(expected);
+ }
+
public class EntityDbo
{
public string Name { get; set; } = string.Empty;
@@ -1711,9 +1748,9 @@ public void DynamicExpressionParser_ParseLambda_CustomType_Method_With_ComplexEx
}
[Theory]
- [InlineData(true, "c => c.Age == 8", "c => (c.Age == 8)")]
+ [InlineData(true, "c => c.Age == 8", "c => (c.Age == Convert(8, Nullable`1))")]
[InlineData(true, "c => c.Name == \"test\"", "c => (c.Name == \"test\")")]
- [InlineData(false, "c => c.Age == 8", "Param_0 => (Param_0.Age == 8)")]
+ [InlineData(false, "c => c.Age == 8", "Param_0 => (Param_0.Age == Convert(8, Nullable`1))")]
[InlineData(false, "c => c.Name == \"test\"", "Param_0 => (Param_0.Name == \"test\")")]
public void DynamicExpressionParser_ParseLambda_RenameParameterExpression(bool renameParameterExpression, string expressionAsString, string expected)
{
@@ -1732,7 +1769,7 @@ public void DynamicExpressionParser_ParseLambda_RenameParameterExpression(bool r
}
[Theory]
- [InlineData("c => c.Age == 8", "([a-z]{16}) =\\> \\(\\1\\.Age == 8\\)")]
+ [InlineData("c => c.Age == 8", "([a-z]{16}) =\\> \\(\\1\\.Age == .+")]
[InlineData("c => c.Name == \"test\"", "([a-z]{16}) =\\> \\(\\1\\.Name == \"test\"\\)")]
public void DynamicExpressionParser_ParseLambda_RenameEmptyParameterExpressionNames(string expressionAsString, string expected)
{
diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs
index e291c34e..f8ccc496 100644
--- a/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs
@@ -159,7 +159,7 @@ public void NumberParser_ParseIntegerLiteral(string text, double expected)
public void NumberParser_ParseDecimalLiteral(string text, char qualifier, decimal expected)
{
// Act
- var result = new NumberParser(_parsingConfig).ParseRealLiteral(text, qualifier, true) as ConstantExpression;
+ var result = new NumberParser(_parsingConfig).ParseRealLiteral(text, text, qualifier, true) as ConstantExpression;
// Assert
result?.Value.Should().Be(expected);
@@ -175,7 +175,7 @@ public void NumberParser_ParseDoubleLiteral(string text, char qualifier, double
// Arrange
// Act
- var result = new NumberParser(_parsingConfig).ParseRealLiteral(text, qualifier, true) as ConstantExpression;
+ var result = new NumberParser(_parsingConfig).ParseRealLiteral(text, text, qualifier, true) as ConstantExpression;
// Assert
result?.Value.Should().Be(expected);
@@ -191,7 +191,7 @@ public void NumberParser_ParseFloatLiteral(string text, char qualifier, float ex
// Arrange
// Act
- var result = new NumberParser(_parsingConfig).ParseRealLiteral(text, qualifier, true) as ConstantExpression;
+ var result = new NumberParser(_parsingConfig).ParseRealLiteral(text, text, qualifier, true) as ConstantExpression;
// Assert
result?.Value.Should().Be(expected);