diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index 9d55da28..7b36f5e9 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -82,7 +82,7 @@ public ExpressionParser([CanBeNull] ParameterExpression[] parameters, [NotNull] _parsingConfig = parsingConfig ?? ParsingConfig.Default; _keywordsHelper = new KeywordsHelper(_parsingConfig); - _textParser = new TextParser(expression); + _textParser = new TextParser(_parsingConfig, expression); _methodFinder = new MethodFinder(_parsingConfig); _expressionHelper = new ExpressionHelper(_parsingConfig); _typeFinder = new TypeFinder(_parsingConfig, _keywordsHelper); @@ -843,7 +843,7 @@ Expression ParseIntegerLiteral() text = text.Substring(2); } - if (!ulong.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, CultureInfo.CurrentCulture, out ulong value)) + if (!ulong.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _parsingConfig.NumberParseCulture, out ulong value)) { throw ParseError(Res.InvalidIntegerLiteral, text); } @@ -872,7 +872,7 @@ Expression ParseIntegerLiteral() text = text.Substring(3); } - if (!long.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, CultureInfo.CurrentCulture, out long value)) + if (!long.TryParse(text, isHexadecimal ? NumberStyles.HexNumber : NumberStyles.Integer, _parsingConfig.NumberParseCulture, out long value)) { throw ParseError(Res.InvalidIntegerLiteral, text); } @@ -924,7 +924,7 @@ Expression TryParseAsFloat(string text, char qualifier) { if (qualifier == 'F' || qualifier == 'f') { - if (float.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Float, CultureInfo.InvariantCulture, out float f)) + if (float.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Float, _parsingConfig.NumberParseCulture, out float f)) { return ConstantExpressionHelper.CreateLiteral(f, text); } @@ -938,7 +938,7 @@ Expression TryParseAsDecimal(string text, char qualifier) { if (qualifier == 'M' || qualifier == 'm') { - if (decimal.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number, CultureInfo.InvariantCulture, out decimal d)) + if (decimal.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number, _parsingConfig.NumberParseCulture, out decimal d)) { return ConstantExpressionHelper.CreateLiteral(d, text); } @@ -953,13 +953,13 @@ Expression TryParseAsDouble(string text, char qualifier) double d; if (qualifier == 'D' || qualifier == 'd') { - if (double.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number, CultureInfo.InvariantCulture, out d)) + if (double.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number, _parsingConfig.NumberParseCulture, out d)) { return ConstantExpressionHelper.CreateLiteral(d, text); } } - if (double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out d)) + if (double.TryParse(text, NumberStyles.Number, _parsingConfig.NumberParseCulture, out d)) { return ConstantExpressionHelper.CreateLiteral(d, text); } diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs index 1633c3ae..3ab902d2 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs @@ -5,6 +5,17 @@ namespace System.Linq.Dynamic.Core.Parser { public class ExpressionPromoter : IExpressionPromoter { + private readonly NumberParser _numberParser; + + /// + /// Initializes a new instance of the class. + /// + /// The ParsingConfig. + public ExpressionPromoter(ParsingConfig config) + { + _numberParser = new NumberParser(config); + } + /// public virtual Expression Promote(Expression expr, Type type, bool exact, bool convertExpr) { @@ -38,7 +49,7 @@ public virtual Expression Promote(Expression expr, Type type, bool exact, bool c case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: - value = TypeHelper.ParseNumber(text, target); + value = _numberParser.ParseNumber(text, target); // Make sure an enum value stays an enum value if (target.IsEnum) @@ -48,7 +59,7 @@ public virtual Expression Promote(Expression expr, Type type, bool exact, bool c break; case TypeCode.Double: - if (target == typeof(decimal)) value = TypeHelper.ParseNumber(text, target); + if (target == typeof(decimal)) value = _numberParser.ParseNumber(text, target); break; case TypeCode.String: @@ -56,9 +67,9 @@ public virtual Expression Promote(Expression expr, Type type, bool exact, bool c break; } #else - if (ce.Type == typeof(Int32) || ce.Type == typeof(UInt32) || ce.Type == typeof(Int64) || ce.Type == typeof(UInt64)) + if (ce.Type == typeof(int) || ce.Type == typeof(uint) || ce.Type == typeof(long) || ce.Type == typeof(ulong)) { - value = TypeHelper.ParseNumber(text, target); + value = _numberParser.ParseNumber(text, target); // Make sure an enum value stays an enum value if (target.GetTypeInfo().IsEnum) @@ -66,14 +77,14 @@ public virtual Expression Promote(Expression expr, Type type, bool exact, bool c value = Enum.ToObject(target, value); } } - else if (ce.Type == typeof(Double)) + else if (ce.Type == typeof(double)) { if (target == typeof(decimal)) { - value = TypeHelper.ParseNumber(text, target); + value = _numberParser.ParseNumber(text, target); } } - else if (ce.Type == typeof(String)) + else if (ce.Type == typeof(string)) { value = TypeHelper.ParseEnum(text, target); } diff --git a/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs b/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs new file mode 100644 index 00000000..4557332a --- /dev/null +++ b/src/System.Linq.Dynamic.Core/Parser/NumberParser.cs @@ -0,0 +1,110 @@ +namespace System.Linq.Dynamic.Core.Parser +{ + /// + /// NumberParser + /// + public class NumberParser + { + private readonly ParsingConfig _config; + + /// + /// Initializes a new instance of the class. + /// + /// The ParsingConfig. + public NumberParser(ParsingConfig config) + { + _config = config; + } + + /// + /// Parses the number (text) into the specified type. + /// + /// The text. + /// The type. + public object ParseNumber(string text, Type type) + { + try + { +#if !(NETFX_CORE || WINDOWS_APP || DOTNET5_1 || UAP10_0 || NETSTANDARD) + switch (Type.GetTypeCode(TypeHelper.GetNonNullableType(type))) + { + case TypeCode.SByte: + return sbyte.Parse(text, _config.NumberParseCulture); + case TypeCode.Byte: + return byte.Parse(text, _config.NumberParseCulture); + case TypeCode.Int16: + return short.Parse(text, _config.NumberParseCulture); + case TypeCode.UInt16: + return ushort.Parse(text, _config.NumberParseCulture); + case TypeCode.Int32: + return int.Parse(text, _config.NumberParseCulture); + case TypeCode.UInt32: + return uint.Parse(text, _config.NumberParseCulture); + case TypeCode.Int64: + return long.Parse(text, _config.NumberParseCulture); + case TypeCode.UInt64: + return ulong.Parse(text, _config.NumberParseCulture); + case TypeCode.Single: + return float.Parse(text, _config.NumberParseCulture); + case TypeCode.Double: + return double.Parse(text, _config.NumberParseCulture); + case TypeCode.Decimal: + return decimal.Parse(text, _config.NumberParseCulture); + } +#else + var tp = TypeHelper.GetNonNullableType(type); + if (tp == typeof(sbyte)) + { + return sbyte.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(byte)) + { + return byte.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(short)) + { + return short.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(ushort)) + { + return ushort.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(int)) + { + return int.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(uint)) + { + return uint.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(long)) + { + return long.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(ulong)) + { + return ulong.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(float)) + { + return float.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(double)) + { + return double.Parse(text, _config.NumberParseCulture); + } + if (tp == typeof(decimal)) + { + return decimal.Parse(text, _config.NumberParseCulture); + } +#endif + } + catch + { + return null; + } + + return null; + } + } +} diff --git a/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs b/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs index a234fe5a..7cde8f2d 100644 --- a/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs +++ b/src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs @@ -185,52 +185,52 @@ public static bool IsCompatibleWith(Type source, Type target) { return false; } - Type sc = st.GetTypeInfo().IsEnum ? typeof(Object) : st; - Type tc = tt.GetTypeInfo().IsEnum ? typeof(Object) : tt; + Type sc = st.GetTypeInfo().IsEnum ? typeof(object) : st; + Type tc = tt.GetTypeInfo().IsEnum ? typeof(object) : tt; - if (sc == typeof(SByte)) + if (sc == typeof(sbyte)) { - if (tc == typeof(SByte) || tc == typeof(Int16) || tc == typeof(Int32) || tc == typeof(Int64) || tc == typeof(Single) || tc == typeof(Double) || tc == typeof(Decimal)) + if (tc == typeof(sbyte) || tc == typeof(short) || tc == typeof(int) || tc == typeof(long) || tc == typeof(float) || tc == typeof(double) || tc == typeof(decimal)) return true; } - else if (sc == typeof(Byte)) + else if (sc == typeof(byte)) { - if (tc == typeof(Byte) || tc == typeof(Int16) || tc == typeof(UInt16) || tc == typeof(Int32) || tc == typeof(UInt32) || tc == typeof(Int64) || tc == typeof(UInt64) || tc == typeof(Single) || tc == typeof(Double) || tc == typeof(Decimal)) + if (tc == typeof(byte) || tc == typeof(short) || tc == typeof(ushort) || tc == typeof(int) || tc == typeof(uint) || tc == typeof(long) || tc == typeof(ulong) || tc == typeof(float) || tc == typeof(double) || tc == typeof(decimal)) return true; } - else if (sc == typeof(Int16)) + else if (sc == typeof(short)) { - if (tc == typeof(Int16) || tc == typeof(Int32) || tc == typeof(Int64) || tc == typeof(Single) || tc == typeof(Double) || tc == typeof(Decimal)) + if (tc == typeof(short) || tc == typeof(int) || tc == typeof(long) || tc == typeof(float) || tc == typeof(double) || tc == typeof(decimal)) return true; } - else if (sc == typeof(UInt16)) + else if (sc == typeof(ushort)) { - if (tc == typeof(UInt16) || tc == typeof(Int32) || tc == typeof(UInt32) || tc == typeof(Int64) || tc == typeof(UInt64) || tc == typeof(Single) || tc == typeof(Double) || tc == typeof(Decimal)) + if (tc == typeof(ushort) || tc == typeof(int) || tc == typeof(uint) || tc == typeof(long) || tc == typeof(ulong) || tc == typeof(float) || tc == typeof(double) || tc == typeof(decimal)) return true; } - else if (sc == typeof(Int32)) + else if (sc == typeof(int)) { - if (tc == typeof(Int32) || tc == typeof(Int64) || tc == typeof(Single) || tc == typeof(Double) || tc == typeof(Decimal)) + if (tc == typeof(int) || tc == typeof(long) || tc == typeof(float) || tc == typeof(double) || tc == typeof(decimal)) return true; } - else if (sc == typeof(UInt32)) + else if (sc == typeof(uint)) { - if (tc == typeof(UInt32) || tc == typeof(Int64) || tc == typeof(UInt64) || tc == typeof(Single) || tc == typeof(Double) || tc == typeof(Decimal)) + if (tc == typeof(uint) || tc == typeof(long) || tc == typeof(ulong) || tc == typeof(float) || tc == typeof(double) || tc == typeof(decimal)) return true; } - else if (sc == typeof(Int64)) + else if (sc == typeof(long)) { - if (tc == typeof(Int64) || tc == typeof(Single) || tc == typeof(Double) || tc == typeof(Decimal)) + if (tc == typeof(long) || tc == typeof(float) || tc == typeof(double) || tc == typeof(decimal)) return true; } - else if (sc == typeof(UInt64)) + else if (sc == typeof(ulong)) { - if (tc == typeof(UInt64) || tc == typeof(Single) || tc == typeof(Double) || tc == typeof(Decimal)) + if (tc == typeof(ulong) || tc == typeof(float) || tc == typeof(double) || tc == typeof(decimal)) return true; } - else if (sc == typeof(Single)) + else if (sc == typeof(float)) { - if (tc == typeof(Single) || tc == typeof(Double)) + if (tc == typeof(float) || tc == typeof(double)) return true; } @@ -313,11 +313,11 @@ private static int GetNumericTypeKind(Type type) return 0; } - if (type == typeof(Char) || type == typeof(Single) || type == typeof(Double) || type == typeof(Decimal)) + if (type == typeof(char) || type == typeof(float) || type == typeof(double) || type == typeof(decimal)) return 1; - if (type == typeof(SByte) || type == typeof(Int16) || type == typeof(Int32) || type == typeof(Int64)) + if (type == typeof(sbyte) || type == typeof(short) || type == typeof(int) || type == typeof(long)) return 2; - if (type == typeof(Byte) || type == typeof(UInt16) || type == typeof(UInt32) || type == typeof(UInt64)) + if (type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong)) return 3; return 0; @@ -390,117 +390,6 @@ private static void AddInterface(List types, Type type) } } - public static object ParseNumber(string text, Type type) - { -#if !(NETFX_CORE || WINDOWS_APP || DOTNET5_1 || UAP10_0 || NETSTANDARD) - switch (Type.GetTypeCode(GetNonNullableType(type))) - { - case TypeCode.SByte: - sbyte sb; - if (SByte.TryParse(text, out sb)) return sb; - break; - case TypeCode.Byte: - byte b; - if (Byte.TryParse(text, out b)) return b; - break; - case TypeCode.Int16: - short s; - if (Int16.TryParse(text, out s)) return s; - break; - case TypeCode.UInt16: - ushort us; - if (UInt16.TryParse(text, out us)) return us; - break; - case TypeCode.Int32: - int i; - if (Int32.TryParse(text, out i)) return i; - break; - case TypeCode.UInt32: - uint ui; - if (UInt32.TryParse(text, out ui)) return ui; - break; - case TypeCode.Int64: - long l; - if (Int64.TryParse(text, out l)) return l; - break; - case TypeCode.UInt64: - ulong ul; - if (UInt64.TryParse(text, out ul)) return ul; - break; - case TypeCode.Single: - float f; - if (Single.TryParse(text, out f)) return f; - break; - case TypeCode.Double: - double d; - if (Double.TryParse(text, out d)) return d; - break; - case TypeCode.Decimal: - decimal e; - if (Decimal.TryParse(text, out e)) return e; - break; - } -#else - var tp = GetNonNullableType(type); - if (tp == typeof(SByte)) - { - sbyte sb; - if (sbyte.TryParse(text, out sb)) return sb; - } - else if (tp == typeof(Byte)) - { - byte b; - if (byte.TryParse(text, out b)) return b; - } - else if (tp == typeof(Int16)) - { - short s; - if (short.TryParse(text, out s)) return s; - } - else if (tp == typeof(UInt16)) - { - ushort us; - if (ushort.TryParse(text, out us)) return us; - } - else if (tp == typeof(Int32)) - { - int i; - if (int.TryParse(text, out i)) return i; - } - else if (tp == typeof(UInt32)) - { - uint ui; - if (uint.TryParse(text, out ui)) return ui; - } - else if (tp == typeof(Int64)) - { - long l; - if (long.TryParse(text, out l)) return l; - } - else if (tp == typeof(UInt64)) - { - ulong ul; - if (ulong.TryParse(text, out ul)) return ul; - } - else if (tp == typeof(Single)) - { - float f; - if (float.TryParse(text, out f)) return f; - } - else if (tp == typeof(Double)) - { - double d; - if (double.TryParse(text, out d)) return d; - } - else if (tp == typeof(Decimal)) - { - decimal e; - if (decimal.TryParse(text, out e)) return e; - } -#endif - return null; - } - public static object ParseEnum(string value, Type type) { if (type.GetTypeInfo().IsEnum && Enum.IsDefined(type, value)) diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index 4651a35d..4ad9bea7 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -1,4 +1,5 @@ -using System.Linq.Dynamic.Core.CustomTypeProviders; +using System.Globalization; +using System.Linq.Dynamic.Core.CustomTypeProviders; using System.Linq.Dynamic.Core.Parser; namespace System.Linq.Dynamic.Core @@ -56,10 +57,7 @@ public IDynamicLinkCustomTypeProvider CustomTypeProvider /// public IExpressionPromoter ExpressionPromoter { - get - { - return _expressionPromoter ?? (_expressionPromoter = new ExpressionPromoter()); - } + get => _expressionPromoter ?? (_expressionPromoter = new ExpressionPromoter(this)); set { @@ -168,5 +166,12 @@ public IQueryableAnalyzer QueryableAnalyzer /// Default value is false. /// public bool DateTimeIsParsedAsUTC { get; set; } = false; + + /// + /// The number parsing culture. + /// + /// Default value is CultureInfo.InvariantCulture + /// + public CultureInfo NumberParseCulture { get; set; } = CultureInfo.InvariantCulture; } } diff --git a/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs b/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs index e4c821eb..8f63cc0d 100644 --- a/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs +++ b/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs @@ -6,12 +6,12 @@ namespace System.Linq.Dynamic.Core.Tokenizer { internal class TextParser { - private static char NumberDecimalSeparator = '.'; + private const char DefaultNumberDecimalSeparator = '.'; - private static char[] EscapeCharacters = new[] { '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v' }; + private static readonly char[] EscapeCharacters = new[] { '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v' }; // These aliases are supposed to simply the where clause and make it more human readable - private static readonly Dictionary _predefinedOperatorAliases = new Dictionary(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary PredefinedOperatorAliases = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "eq", TokenId.Equal }, { "equal", TokenId.Equal }, @@ -34,6 +34,8 @@ internal class TextParser { "mod", TokenId.Percent } }; + private readonly ParsingConfig _config; + private readonly char _numberDecimalSeparator; private readonly string _text; private readonly int _textLen; @@ -41,10 +43,14 @@ internal class TextParser private char _ch; public Token CurrentToken; - public TextParser(string text) + public TextParser(ParsingConfig config, string text) { + _config = config; + _numberDecimalSeparator = config.NumberParseCulture?.NumberFormat.NumberDecimalSeparator[0] ?? DefaultNumberDecimalSeparator; + _text = text; _textLen = _text.Length; + SetTextPos(0); NextToken(); } @@ -63,6 +69,7 @@ private void NextChar() } _ch = _textPos < _textLen ? _text[_textPos] : '\0'; } + public char PeekNextChar() { if (_textPos + 1 < _textLen) @@ -362,7 +369,7 @@ public void NextToken() break; } - if (_ch == NumberDecimalSeparator) + if (_ch == _numberDecimalSeparator) { tokenId = TokenId.RealLiteral; NextChar(); @@ -458,7 +465,7 @@ private static Exception ParseError(int pos, string format, params object[] args private static TokenId GetAliasedTokenId(TokenId tokenId, string alias) { - return tokenId == TokenId.Identifier && _predefinedOperatorAliases.TryGetValue(alias, out TokenId id) ? id : tokenId; + return tokenId == TokenId.Identifier && PredefinedOperatorAliases.TryGetValue(alias, out TokenId id) ? id : tokenId; } private static bool IsHexChar(char c) diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs new file mode 100644 index 00000000..79450df8 --- /dev/null +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/NumberParserTests.cs @@ -0,0 +1,42 @@ +using System.Globalization; +using NFluent; +using System.Linq.Dynamic.Core.Parser; +using Xunit; + +namespace System.Linq.Dynamic.Core.Tests.Parser +{ + public class NumberParserTests + { + private readonly ParsingConfig _parsingConfig = new ParsingConfig(); + + private readonly NumberParser _sut; + + public NumberParserTests() + { + _sut = new NumberParser(_parsingConfig); + } + + [Fact] + public void NumberParser_ParseNumber_Decimal_With_DefaultCulture() + { + // Act + var result = _sut.ParseNumber("3.21", typeof(decimal)); + + // Assert + Check.That(result).Equals(3.21m); + } + + [Fact] + public void NumberParser_ParseNumber_Decimal_With_GermanCulture() + { + // Assign + _parsingConfig.NumberParseCulture = CultureInfo.CreateSpecificCulture("de-DE"); + + // Act + var result = _sut.ParseNumber("3,21", typeof(decimal)); + + // Assert + Check.That(result).Equals(3.21m); + } + } +} diff --git a/test/System.Linq.Dynamic.Core.Tests/Tokenizer/TextParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/Tokenizer/TextParserTests.cs index de4448ed..e67bed2c 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Tokenizer/TextParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Tokenizer/TextParserTests.cs @@ -1,4 +1,5 @@ using NFluent; +using System.Globalization; using System.Linq.Dynamic.Core.Exceptions; using System.Linq.Dynamic.Core.Tokenizer; using Xunit; @@ -7,11 +8,13 @@ namespace System.Linq.Dynamic.Core.Tests.Tokenizer { public class TextParserTests { + private readonly ParsingConfig _config = new ParsingConfig(); + [Fact] public void TextParser_Parse_Bar() { // Assign + Act - var textParser = new TextParser(" | "); + var textParser = new TextParser(_config, " | "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.Bar); @@ -23,7 +26,7 @@ public void TextParser_Parse_Bar() public void TextParser_Parse_Colon() { // Assign + Act - var textParser = new TextParser(" : "); + var textParser = new TextParser(_config, " : "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.Colon); @@ -35,7 +38,7 @@ public void TextParser_Parse_Colon() public void TextParser_Parse_Exclamation() { // Assign + Act - var textParser = new TextParser(" !x"); + var textParser = new TextParser(_config, " !x"); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.Exclamation); @@ -47,7 +50,7 @@ public void TextParser_Parse_Exclamation() public void TextParser_Parse_ExclamationEqual() { // Assign + Act - var textParser = new TextParser(" != 1"); + var textParser = new TextParser(_config, " != 1"); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.ExclamationEqual); @@ -59,7 +62,7 @@ public void TextParser_Parse_ExclamationEqual() public void TextParser_Parse_GreaterThanEqual() { // Assign + Act - var textParser = new TextParser(" >= "); + var textParser = new TextParser(_config, " >= "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.GreaterThanEqual); @@ -71,21 +74,21 @@ public void TextParser_Parse_GreaterThanEqual() public void TextParser_Parse_HexadecimalIntegerLiteral() { // Assign + Act - var textParser = new TextParser(" 0x1234567890AbCdEfL "); + var textParser = new TextParser(_config, " 0x1234567890AbCdEfL "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.IntegerLiteral); Check.That(textParser.CurrentToken.Pos).Equals(1); Check.That(textParser.CurrentToken.Text).Equals("0x1234567890AbCdEfL"); - Check.ThatCode(() => new TextParser("0xz1234")).Throws(); + Check.ThatCode(() => new TextParser(_config, "0xz1234")).Throws(); } [Fact] public void TextParser_Parse_LessGreater() { // Assign + Act - var textParser = new TextParser(" <> "); + var textParser = new TextParser(_config, " <> "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.LessGreater); @@ -97,7 +100,7 @@ public void TextParser_Parse_LessGreater() public void TextParser_Parse_NullPropagation() { // Assign + Act - var textParser = new TextParser(" ?. "); + var textParser = new TextParser(_config, " ?. "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.NullPropagation); @@ -109,21 +112,21 @@ public void TextParser_Parse_NullPropagation() public void TextParser_Parse_RealLiteral() { // Assign + Act - var textParser = new TextParser(" 1.0E25 "); + var textParser = new TextParser(_config, " 1.0E25 "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.RealLiteral); Check.That(textParser.CurrentToken.Pos).Equals(1); Check.That(textParser.CurrentToken.Text).Equals("1.0E25"); - Check.ThatCode(() => new TextParser("1.e25")).Throws(); + Check.ThatCode(() => new TextParser(_config, "1.e25")).Throws(); } [Fact] public void TextParser_Parse_RealLiteralDecimalQualifier() { // Assign + Act - var textParser = new TextParser(" 12.5m "); + var textParser = new TextParser(_config, " 12.5m "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.RealLiteral); @@ -131,11 +134,29 @@ public void TextParser_Parse_RealLiteralDecimalQualifier() Check.That(textParser.CurrentToken.Text).Equals("12.5m"); } + [Fact] + public void TextParser_Parse_RealLiteralDecimalQualifier_Other_NumberDecimalSeparator() + { + // Assign + var config = new ParsingConfig + { + NumberParseCulture = CultureInfo.CreateSpecificCulture("de-DE") + }; + + // Act + var textParser = new TextParser(config, " 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 "); + var textParser = new TextParser(_config, " 12.5f "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.RealLiteral); @@ -147,7 +168,7 @@ public void TextParser_Parse_RealLiteralFloatQualifier() public void TextParser_Parse_RealLiteralMinus() { // Assign + Act - var textParser = new TextParser(" 1.0E-25 "); + var textParser = new TextParser(_config, " 1.0E-25 "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.RealLiteral); @@ -159,7 +180,7 @@ public void TextParser_Parse_RealLiteralMinus() public void TextParser_Parse_RealLiteralPlus() { // Assign + Act - var textParser = new TextParser(" 1.0E+25 "); + var textParser = new TextParser(_config, " 1.0E+25 "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.RealLiteral); @@ -171,7 +192,7 @@ public void TextParser_Parse_RealLiteralPlus() public void TextParser_Parse_Percent() { // Assign + Act - var textParser = new TextParser(" % "); + var textParser = new TextParser(_config, " % "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.Percent); @@ -183,7 +204,7 @@ public void TextParser_Parse_Percent() public void TextParser_Parse_Slash() { // Assign + Act - var textParser = new TextParser(" / "); + var textParser = new TextParser(_config, " / "); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.Slash); @@ -195,7 +216,7 @@ public void TextParser_Parse_Slash() public void TextParser_Parse_StringLiteral_WithSingleQuotes_Backslash() { // Assign + Act - var textParser = new TextParser("'\\'"); + var textParser = new TextParser(_config, "'\\'"); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.StringLiteral); @@ -207,7 +228,7 @@ public void TextParser_Parse_StringLiteral_WithSingleQuotes_Backslash() public void TextParser_Parse_StringLiteral_WithSingleQuotes_DoubleQuote() { // Assign + Act - var textParser = new TextParser("'\"'"); + var textParser = new TextParser(_config, "'\"'"); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.StringLiteral); @@ -219,7 +240,7 @@ public void TextParser_Parse_StringLiteral_WithSingleQuotes_DoubleQuote() public void TextParser_Parse_StringLiteral_WithSingleQuotes_SingleQuote() { // Assign + Act - var textParser = new TextParser("'\''"); + var textParser = new TextParser(_config, "'\''"); // Assert Check.That(textParser.CurrentToken.Id).Equals(TokenId.StringLiteral); @@ -232,7 +253,7 @@ public void TextParser_Parse_ThrowsException() { // Assign + Act + Assert //Check.ThatCode(() => { new TextParser("ಬಾ"); }).Throws(); - Check.ThatCode(() => { new TextParser(";"); }).Throws(); + Check.ThatCode(() => { new TextParser(_config, ";"); }).Throws(); } } }