Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support to inject custom converters #83

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/DynamicExpresso.Core/Converters/IConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace DynamicExpresso.Converters
{
public interface IConverter
{
object Convert(string text);
}
}
41 changes: 41 additions & 0 deletions src/DynamicExpresso.Core/Converters/IntegerConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Globalization;

namespace DynamicExpresso.Converters
{
public class IntegerConverter : IConverter
{
private const NumberStyles ParseLiteralUnsignedNumberStyle = NumberStyles.AllowLeadingSign;
private const NumberStyles ParseLiteralNumberStyle = NumberStyles.AllowLeadingSign;

private static readonly CultureInfo ParseCulture = CultureInfo.InvariantCulture;

public virtual object Convert(string text)
{
if (text[0] != '-')
{
if (!ulong.TryParse(text, ParseLiteralUnsignedNumberStyle, ParseCulture, out ulong value))
throw new Exception();

if (value <= int.MaxValue)
return (int)value;
if (value <= uint.MaxValue)
return (uint)value;
if (value <= long.MaxValue)
return (long)value;

return value;
}
else
{
if (!long.TryParse(text, ParseLiteralNumberStyle, ParseCulture, out long value))
throw new Exception();

if (value >= int.MinValue && value <= int.MaxValue)
return (int)value;

return value;
}
}
}
}
41 changes: 41 additions & 0 deletions src/DynamicExpresso.Core/Converters/RealConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Globalization;

namespace DynamicExpresso.Converters
{
public class RealConverter : IConverter
{
private const NumberStyles ParseLiteralUnsignedNumberStyle = NumberStyles.AllowLeadingSign;
private const NumberStyles ParseLiteralNumberStyle = NumberStyles.AllowLeadingSign;
private const NumberStyles ParseLiteralDecimalNumberStyle = NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint;

private static readonly CultureInfo ParseCulture = CultureInfo.InvariantCulture;

public virtual object Convert(string text)
{
object value = null;
var last = text[text.Length - 1];

if (last == 'F' || last == 'f')
{
if (float.TryParse(text.Substring(0, text.Length - 1), ParseLiteralDecimalNumberStyle, ParseCulture, out float f))
value = f;
}
else if (last == 'M' || last == 'm')
{
if (decimal.TryParse(text.Substring(0, text.Length - 1), ParseLiteralDecimalNumberStyle, ParseCulture, out decimal dc))
value = dc;
}
else
{
if (double.TryParse(text, ParseLiteralDecimalNumberStyle, ParseCulture, out double d))
value = d;
}

if (value == null)
throw new Exception();

return value;
}
}
}
17 changes: 15 additions & 2 deletions src/DynamicExpresso.Core/Interpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Linq.Expressions;
using DynamicExpresso.Exceptions;
using DynamicExpresso.Converters;

namespace DynamicExpresso
{
Expand Down Expand Up @@ -304,6 +305,11 @@ public Lambda Parse(string expressionText, params Parameter[] parameters)
return Parse(expressionText, typeof(void), parameters);
}

public Lambda Parse(string expressionText, IConverter integerConverter, IConverter realConverter, params Parameter[] parameters)
{
return Parse(expressionText, typeof(void), integerConverter, realConverter, parameters);
}

/// <summary>
/// Parse a text expression and returns a Lambda class that can be used to invoke it.
/// If the expression cannot be converted to the type specified in the expressionType parameter
Expand All @@ -319,6 +325,11 @@ public Lambda Parse(string expressionText, Type expressionType, params Parameter
return ParseAsLambda(expressionText, expressionType, parameters);
}

public Lambda Parse(string expressionText, Type expressionType, IConverter integerConverter, IConverter realConverter, params Parameter[] parameters)
{
return ParseAsLambda(expressionText, expressionType, parameters, integerConverter, realConverter);
}

[Obsolete("Use ParseAsDelegate<TDelegate>(string, params string[])")]
public TDelegate Parse<TDelegate>(string expressionText, params string[] parametersNames)
{
Expand Down Expand Up @@ -408,13 +419,15 @@ public IdentifiersInfo DetectIdentifiers(string expression)

#region Private methods

private Lambda ParseAsLambda(string expressionText, Type expressionType, Parameter[] parameters)
private Lambda ParseAsLambda(string expressionText, Type expressionType, Parameter[] parameters, IConverter integerConverter = null, IConverter realConverter = null)
{
var arguments = new ParserArguments(
expressionText,
_settings,
expressionType,
parameters);
parameters,
integerConverter,
realConverter);

var expression = Parser.Parse(arguments);

Expand Down
18 changes: 17 additions & 1 deletion src/DynamicExpresso.Core/ParserArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq.Expressions;
using System.Reflection;
using DynamicExpresso.Exceptions;
using DynamicExpresso.Converters;

namespace DynamicExpresso
{
Expand All @@ -16,7 +17,7 @@ internal class ParserArguments
private readonly HashSet<ReferenceType> _usedTypes = new HashSet<ReferenceType>();
private readonly HashSet<Identifier> _usedIdentifiers = new HashSet<Identifier>();

public ParserArguments(
private ParserArguments(
string expressionText,
ParserSettings settings,
Type expressionReturnType,
Expand All @@ -41,6 +42,21 @@ IEnumerable<Parameter> declaredParameters
}
}

public ParserArguments(
string expressionText,
ParserSettings settings,
Type expressionReturnType,
IEnumerable<Parameter> declaredParameters,
IConverter intergerConverter,
IConverter realConverter) : this(expressionText, settings, expressionReturnType, declaredParameters)
{
IntegerConverter = intergerConverter ?? new IntegerConverter();
RealConverter = realConverter ?? new RealConverter();
}

public IConverter IntegerConverter { get; }
public IConverter RealConverter { get; }

public ParserSettings Settings { get; private set;}
public string ExpressionText { get; private set; }
public Type ExpressionReturnType { get; private set; }
Expand Down
60 changes: 15 additions & 45 deletions src/DynamicExpresso.Core/Parsing/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ public static Expression Parse(ParserArguments arguments)
return new Parser(arguments).Parse();
}

private const NumberStyles ParseLiteralNumberStyle = NumberStyles.AllowLeadingSign;
private const NumberStyles ParseLiteralUnsignedNumberStyle = NumberStyles.AllowLeadingSign;
private const NumberStyles ParseLiteralDecimalNumberStyle = NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint;
private static readonly CultureInfo ParseCulture = CultureInfo.InvariantCulture;

private readonly ParserArguments _arguments;
Expand Down Expand Up @@ -517,65 +514,38 @@ private Expression ParseIntegerLiteral()
{
ValidateToken(TokenId.IntegerLiteral);
var text = _token.text;
if (text[0] != '-')

try
{
if (!ulong.TryParse(text, ParseLiteralUnsignedNumberStyle, ParseCulture, out ulong value))
throw CreateParseException(_token.pos, ErrorMessages.InvalidIntegerLiteral, text);
var number = _arguments.IntegerConverter.Convert(text);

NextToken();

if (value <= int.MaxValue)
return CreateLiteral((int)value);
if (value <= uint.MaxValue)
return CreateLiteral((uint)value);
if (value <= long.MaxValue)
return CreateLiteral((long)value);

return CreateLiteral(value);
return CreateLiteral(number);
}
else
catch
{
if (!long.TryParse(text, ParseLiteralNumberStyle, ParseCulture, out long value))
throw CreateParseException(_token.pos, ErrorMessages.InvalidIntegerLiteral, text);

NextToken();

if (value >= int.MinValue && value <= int.MaxValue)
return CreateLiteral((int)value);

return CreateLiteral(value);
throw CreateParseException(_token.pos, ErrorMessages.InvalidIntegerLiteral, text);
}
}

private Expression ParseRealLiteral()
{
ValidateToken(TokenId.RealLiteral);
var text = _token.text;
object value = null;
var last = text[text.Length - 1];

if (last == 'F' || last == 'f')
{
if (float.TryParse(text.Substring(0, text.Length - 1), ParseLiteralDecimalNumberStyle, ParseCulture, out float f))
value = f;
}
else if (last == 'M' || last == 'm')

try
{
if (decimal.TryParse(text.Substring(0, text.Length - 1), ParseLiteralDecimalNumberStyle, ParseCulture, out decimal dc))
value = dc;
var value = _arguments.RealConverter.Convert(text);

NextToken();

return CreateLiteral(value);
}
else
catch
{
if (double.TryParse(text, ParseLiteralDecimalNumberStyle, ParseCulture, out double d))
value = d;
}

if (value == null)
throw CreateParseException(_token.pos, ErrorMessages.InvalidRealLiteral, text);

NextToken();

return CreateLiteral(value);
}
}

private static Expression CreateLiteral(object value)
Expand Down
33 changes: 33 additions & 0 deletions test/DynamicExpresso.UnitTest/ConvertersTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using DynamicExpresso.Exceptions;
using NUnit.Framework;

namespace DynamicExpresso.UnitTest
{
[TestFixture]
public class ConvertersTest
{
class IntegerConveter : Converters.IConverter
{
public object Convert(string text)
{
if (!decimal.TryParse(text, out decimal result))
throw new Exception();

return result;
}
}

[Test]
public void Multiplicate_Nullable_Decimal_By_Integer_Number()
{
var target = new Interpreter();

var expression = target.Parse("x * y * 100",
new IntegerConveter(), null,
new Parameter("x", typeof(decimal)), new Parameter("y", typeof(decimal?)));

Assert.AreEqual(7 * 3 * 100, expression.Invoke(7M, 3M));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,9 @@
<ItemGroup>
<ProjectReference Include="..\..\src\DynamicExpresso.Core\DynamicExpresso.Core.csproj" />
</ItemGroup>

<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>

</Project>