Skip to content

Commit

Permalink
Adds support for decimal qualifiers. Resolves #91 (#92)
Browse files Browse the repository at this point in the history
* Adds support for decimal qualifiers

* Adds tests for improve code coverage

* Tests ordered alphabetically
  • Loading branch information
pferraris authored and StefH committed Jun 27, 2017
1 parent cbb3900 commit ab249d4
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 7 deletions.
20 changes: 19 additions & 1 deletion src/System.Linq.Dynamic.Core/ExpressionParser.cs
Expand Up @@ -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))
{
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs
Expand Up @@ -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;
}

Expand Down
Expand Up @@ -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<int>),
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]
Expand Down Expand Up @@ -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<int>),
typeof(string),
"it.ToString()");
Assert.Equal(typeof(string), expression.ReturnType);
}

[Fact]
public void ParseLambda_IllegalMethodCall_ThrowsException()
{
Expand Down
33 changes: 33 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
Expand Up @@ -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()
{
Expand Down
24 changes: 24 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/TextParserTests.cs
Expand Up @@ -107,6 +107,30 @@ public void TextParser_Parse_RealLiteral()
Check.ThatCode(() => new TextParser("1.e25")).Throws<ParseException>();
}

[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()
{
Expand Down

0 comments on commit ab249d4

Please sign in to comment.