From ab249d498a28118ff85742ee4b1b442f367d223d Mon Sep 17 00:00:00 2001 From: Pablo Ferraris Date: Tue, 27 Jun 2017 09:14:30 +0200 Subject: [PATCH] Adds support for decimal qualifiers. Resolves #91 (#92) * Adds support for decimal qualifiers * Adds tests for improve code coverage * Tests ordered alphabetically --- .../ExpressionParser.cs | 20 ++++++++++- .../Tokenizer/TextParser.cs | 1 + .../DynamicExpressionParserTests.cs | 29 ++++++++++++---- .../ExpressionTests.cs | 33 +++++++++++++++++++ .../TextParserTests.cs | 24 ++++++++++++++ 5 files changed, 100 insertions(+), 7 deletions(-) diff --git a/src/System.Linq.Dynamic.Core/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/ExpressionParser.cs index e6e43962..7a0d8d3c 100644 --- a/src/System.Linq.Dynamic.Core/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/ExpressionParser.cs @@ -937,7 +937,7 @@ Expression ParseIntegerLiteral() bool isHexadecimal = text.StartsWith(text[0] == '-' ? "-0x" : "0x", StringComparison.CurrentCultureIgnoreCase); char[] qualifierLetters = isHexadecimal ? new[] { 'U', 'u', 'L', 'l' } - : new[] { 'U', 'u', 'L', 'l', 'F', 'f', 'D', 'd' }; + : new[] { 'U', 'u', 'L', 'l', 'F', 'f', 'D', 'd', 'M', 'm' }; if (qualifierLetters.Contains(last)) { @@ -1007,6 +1007,9 @@ Expression ParseIntegerLiteral() if (qualifier == "D" || qualifier == "d") return TryParseAsDouble(text, qualifier[0]); + if (qualifier == "M" || qualifier == "m") + return TryParseAsDecimal(text, qualifier[0]); + throw ParseError(Res.MinusCannotBeAppliedToUnsignedInteger); } @@ -1038,6 +1041,21 @@ Expression TryParseAsFloat(string text, char qualifier) } } + // not possible to find float qualifier, so try to parse as double + return TryParseAsDecimal(text, qualifier); + } + + Expression TryParseAsDecimal(string text, char qualifier) + { + if (qualifier == 'M' || qualifier == 'm') + { + decimal d; + if (decimal.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number, CultureInfo.InvariantCulture, out d)) + { + return CreateLiteral(d, text); + } + } + // not possible to find float qualifier, so try to parse as double return TryParseAsDouble(text, qualifier); } diff --git a/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs b/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs index 19f44f5b..c9d58950 100644 --- a/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs +++ b/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs @@ -346,6 +346,7 @@ public void NextToken() if (_ch == 'F' || _ch == 'f') NextChar(); if (_ch == 'D' || _ch == 'd') NextChar(); + if (_ch == 'M' || _ch == 'm') NextChar(); break; } diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 266b482a..23812ce4 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -203,13 +203,20 @@ public void ParseLambda_ParameterExpressionMethodCall_ReturnsIntExpression() } [Fact] - public void ParseLambda_TupleToStringMethodCall_ReturnsStringLambdaExpression() + public void ParseLambda_RealNumbers() { - var expression = DynamicExpressionParser.ParseLambda( - typeof(Tuple), - typeof(string), - "it.ToString()"); - Assert.Equal(typeof(string), expression.ReturnType); + var parameters = new ParameterExpression[0]; + + var result1 = DynamicExpressionParser.ParseLambda(parameters, typeof(double), "0.10"); + var result2 = DynamicExpressionParser.ParseLambda(parameters, typeof(double), "0.10d"); + var result3 = DynamicExpressionParser.ParseLambda(parameters, typeof(float), "0.10f"); + var result4 = DynamicExpressionParser.ParseLambda(parameters, typeof(decimal), "0.10m"); + + // Assert + Assert.Equal(0.10d, result1.Compile().DynamicInvoke()); + Assert.Equal(0.10d, result2.Compile().DynamicInvoke()); + Assert.Equal(0.10f, result3.Compile().DynamicInvoke()); + Assert.Equal(0.10m, result4.Compile().DynamicInvoke()); } [Fact] @@ -279,6 +286,16 @@ public void ParseLambda_StringLiteralEscapedBackslash_ReturnsBooleanLambdaExpres Assert.Equal(expectedRightValue, rightValue); } + [Fact] + public void ParseLambda_TupleToStringMethodCall_ReturnsStringLambdaExpression() + { + var expression = DynamicExpressionParser.ParseLambda( + typeof(Tuple), + typeof(string), + "it.ToString()"); + Assert.Equal(typeof(string), expression.ReturnType); + } + [Fact] public void ParseLambda_IllegalMethodCall_ThrowsException() { diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs index ee60eecf..1158ad9d 100644 --- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs @@ -383,6 +383,39 @@ public void ExpressionTests_DateTimeString() Assert.Equal(lst[0], result2.Single()); } + [Fact] + public void ExpressionTests_DecimalQualifiers() + { + //Arrange + var values = new[] { 1m, 2M, 3M }.AsQueryable(); + var resultValues = new[] { 2m, 3m }.AsQueryable(); + + //Act + var result1 = values.Where("it == 2M or it == 3m"); + var result2 = values.Where("it == 2.0M or it == 3.00m"); + + //Assert + Assert.Equal(resultValues.ToArray(), result1.ToArray()); + Assert.Equal(resultValues.ToArray(), result2.ToArray()); + } + + [Fact] + public void ExpressionTests_DecimalQualifiers_Negative() + { + //Arrange + var values = new[] { -1m, -2M, -3M }.AsQueryable(); + var resultValues = new[] { -2m, -3m }.AsQueryable(); + + //Act + var result1 = values.Where("it == -2M or it == -3m"); + var result2 = values.Where("it == -2.0M or it == -3.0m"); + + //Assert + Assert.Equal(resultValues.ToArray(), result1.ToArray()); + Assert.Equal(resultValues.ToArray(), result2.ToArray()); + } + + [Fact] public void ExpressionTests_DistinctBy() { diff --git a/test/System.Linq.Dynamic.Core.Tests/TextParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/TextParserTests.cs index 4050db01..536f0c75 100644 --- a/test/System.Linq.Dynamic.Core.Tests/TextParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/TextParserTests.cs @@ -107,6 +107,30 @@ public void TextParser_Parse_RealLiteral() Check.ThatCode(() => new TextParser("1.e25")).Throws(); } + [Fact] + public void TextParser_Parse_RealLiteralDecimalQualifier() + { + // Assign + Act + var textParser = new TextParser(" 12.5m "); + + // Assert + Check.That(textParser.CurrentToken.Id).Equals(TokenId.RealLiteral); + Check.That(textParser.CurrentToken.Pos).Equals(1); + Check.That(textParser.CurrentToken.Text).Equals("12.5m"); + } + + [Fact] + public void TextParser_Parse_RealLiteralFloatQualifier() + { + // Assign + Act + var textParser = new TextParser(" 12.5f "); + + // Assert + Check.That(textParser.CurrentToken.Id).Equals(TokenId.RealLiteral); + Check.That(textParser.CurrentToken.Pos).Equals(1); + Check.That(textParser.CurrentToken.Text).Equals("12.5f"); + } + [Fact] public void TextParser_Parse_RealLiteralMinus() {