Skip to content

Commit 178569e

Browse files
authored
Add support for hexadecimal and binary literals. (#238)
Fixes #236
1 parent 055f156 commit 178569e

File tree

2 files changed

+85
-8
lines changed

2 files changed

+85
-8
lines changed

src/DynamicExpresso.Core/Parsing/Parser.cs

+56-7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public static Expression Parse(ParserArguments arguments)
2828

2929
private const NumberStyles ParseLiteralNumberStyle = NumberStyles.AllowLeadingSign;
3030
private const NumberStyles ParseLiteralUnsignedNumberStyle = NumberStyles.AllowLeadingSign;
31+
private const NumberStyles ParseLiteralHexNumberStyle = NumberStyles.HexNumber;
3132
private const NumberStyles ParseLiteralDecimalNumberStyle = NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint;
3233
private const NumberStyles ParseLiteralDoubleNumberStyle = NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent;
3334
private static readonly CultureInfo ParseCulture = CultureInfo.InvariantCulture;
@@ -877,12 +878,31 @@ private Expression ParseIntegerLiteral()
877878

878879
text = text.Substring(0, numberEnd + 1);
879880

880-
// No suffix find, verify if DefaultNumberType.Long is specified
881+
// No suffix found, verify if DefaultNumberType.Long is specified
881882
if (_defaultNumberType == DefaultNumberType.Long) isLong = true;
882883

883884
if (text[0] != '-')
884885
{
885-
if (!ulong.TryParse(text, ParseLiteralUnsignedNumberStyle, ParseCulture, out ulong value))
886+
ulong value;
887+
if (text.StartsWith("0x") || text.StartsWith("0X"))
888+
{
889+
var hex = text.Substring(2);
890+
if (!ulong.TryParse(hex, ParseLiteralHexNumberStyle, ParseCulture, out value))
891+
throw CreateParseException(_token.pos, ErrorMessages.InvalidIntegerLiteral, text);
892+
}
893+
else if (text.StartsWith("0b") || text.StartsWith("0B"))
894+
{
895+
var binary = text.Substring(2);
896+
try
897+
{
898+
value = Convert.ToUInt64(binary, 2);
899+
}
900+
catch (FormatException ex)
901+
{
902+
throw WrapWithParseException(_token.pos, ErrorMessages.InvalidIntegerLiteral, ex, text);
903+
}
904+
}
905+
else if (!ulong.TryParse(text, ParseLiteralUnsignedNumberStyle, ParseCulture, out value))
886906
throw CreateParseException(_token.pos, ErrorMessages.InvalidIntegerLiteral, text);
887907

888908
NextToken();
@@ -3005,12 +3025,39 @@ private void NextToken()
30053025
t = TokenId.IntegerLiteral;
30063026
}
30073027

3028+
// binary and hexadecimal integer literals
3029+
var canBeRealLiteral = true;
3030+
if (_parseChar == '0')
3031+
{
3032+
NextChar();
3033+
if (_parseChar == 'x' || _parseChar == 'X')
3034+
{
3035+
canBeRealLiteral = false;
3036+
do
3037+
{
3038+
NextChar();
3039+
} while (char.IsDigit(_parseChar) || (_parseChar >= 'a' && _parseChar <= 'f') || (_parseChar >= 'A' && _parseChar <= 'F'));
3040+
}
3041+
else if (_parseChar == 'b' || _parseChar == 'B')
3042+
{
3043+
canBeRealLiteral = false;
3044+
do
3045+
{
3046+
NextChar();
3047+
} while (_parseChar == '0' || _parseChar == '1');
3048+
}
3049+
else
3050+
{
3051+
PreviousChar();
3052+
}
3053+
}
3054+
30083055
do
30093056
{
30103057
NextChar();
30113058
} while (char.IsDigit(_parseChar));
30123059

3013-
if (_parseChar == '.')
3060+
if (canBeRealLiteral && _parseChar == '.')
30143061
{
30153062
NextChar();
30163063
if (char.IsDigit(_parseChar))
@@ -3028,7 +3075,7 @@ private void NextToken()
30283075
}
30293076
}
30303077

3031-
if (_parseChar == 'E' || _parseChar == 'e')
3078+
if (canBeRealLiteral && (_parseChar == 'E' || _parseChar == 'e'))
30323079
{
30333080
t = TokenId.RealLiteral;
30343081
NextChar();
@@ -3041,7 +3088,7 @@ private void NextToken()
30413088
} while (char.IsDigit(_parseChar));
30423089
}
30433090

3044-
if (_parseChar == 'D' || _parseChar == 'd' || _parseChar == 'F' || _parseChar == 'f' || _parseChar == 'M' || _parseChar == 'm')
3091+
if (canBeRealLiteral && (_parseChar == 'D' || _parseChar == 'd' || _parseChar == 'F' || _parseChar == 'f' || _parseChar == 'M' || _parseChar == 'm'))
30453092
{
30463093
t = TokenId.RealLiteral;
30473094
NextChar();
@@ -3050,12 +3097,14 @@ private void NextToken()
30503097
// 'U' | 'u' | 'L' | 'l' | 'UL' | 'Ul' | 'uL' | 'ul' | 'LU' | 'Lu' | 'lU' | 'lu'
30513098
if (_parseChar == 'U' || _parseChar == 'u')
30523099
{
3100+
t = TokenId.IntegerLiteral;
30533101
NextChar();
30543102
if (_parseChar == 'L' || _parseChar == 'l')
30553103
NextChar();
30563104
}
30573105
else if (_parseChar == 'L' || _parseChar == 'l')
30583106
{
3107+
t = TokenId.IntegerLiteral;
30593108
NextChar();
30603109
if (_parseChar == 'U' || _parseChar == 'u')
30613110
NextChar();
@@ -3104,9 +3153,9 @@ private void ValidateToken(TokenId t)
31043153
throw CreateParseException(_token.pos, ErrorMessages.SyntaxError);
31053154
}
31063155

3107-
private static Exception WrapWithParseException(int pos, string msg, Exception ex)
3156+
private static Exception WrapWithParseException(int pos, string format, Exception ex, params object[] args)
31083157
{
3109-
return new ParseException(msg, pos, ex);
3158+
return new ParseException(string.Format(format, args), pos, ex);
31103159
}
31113160

31123161
private static Exception CreateParseException(int pos, string format, params object[] args)

test/DynamicExpresso.UnitTest/LiteralsTest.cs

+29-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using NUnit.Framework;
1+
using NUnit.Framework;
22
using System.Threading;
33
using System.Globalization;
44
using DynamicExpresso.Exceptions;
@@ -353,6 +353,34 @@ public void Invalid_Numeric_Literals_wrong_suffix_x()
353353
Assert.Throws<ParseException>(() => target.Eval("45G"));
354354
}
355355

356+
[Test]
357+
public void Binary_Literals()
358+
{
359+
var target = new Interpreter();
360+
361+
Assert.AreEqual(0b101ul, target.Eval("0b101ul"));
362+
Assert.AreEqual(0B1111L, target.Eval("0B1111l"));
363+
Assert.AreEqual(6, target.Eval("4 + 0b10"));
364+
365+
Assert.Throws<ParseException>(() => target.Eval("0b12"));
366+
Assert.Throws<ParseException>(() => target.Eval("0b10.10"));
367+
Assert.Throws<ParseException>(() => target.Eval("0b10d"));
368+
Assert.Throws<ParseException>(() => target.Eval("0b10e"));
369+
}
370+
371+
[Test]
372+
public void Hexadecimal_Literals()
373+
{
374+
var target = new Interpreter();
375+
376+
Assert.AreEqual(0x012EFul, target.Eval("0x012EFul"));
377+
Assert.AreEqual(0XAAe2L, target.Eval("0XAAe2l"));
378+
Assert.AreEqual(165, target.Eval("4 + 0xA1"));
379+
380+
Assert.Throws<ParseException>(() => target.Eval("0x1Gl"));
381+
Assert.Throws<ParseException>(() => target.Eval("0x12.12"));
382+
}
383+
356384
[Test]
357385
public void Calling_System_Method_On_Literals()
358386
{

0 commit comments

Comments
 (0)