Browse Source

Add: HexConverter

AnnulusGames 1 year ago
parent
commit
b8485ede51

+ 15 - 2
src/Lua/CodeAnalysis/Syntax/Parser.cs

@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 using Lua.Internal;
 using Lua.CodeAnalysis.Syntax.Nodes;
+using System.Globalization;
 
 namespace Lua.CodeAnalysis.Syntax;
 
@@ -559,7 +560,7 @@ public ref struct Parser
                 SyntaxTokenType.LSquare or SyntaxTokenType.Dot or SyntaxTokenType.Colon => ParseTableAccessExpression(ref enumerator, null),
                 _ => new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position),
             },
-            SyntaxTokenType.Number => new NumericLiteralNode(double.Parse(enumerator.Current.Text.Span), enumerator.Current.Position),
+            SyntaxTokenType.Number => new NumericLiteralNode(ConvertTextToNumber(enumerator.Current.Text.Span), enumerator.Current.Position),
             SyntaxTokenType.String => new StringLiteralNode(enumerator.Current.Text.ToString(), enumerator.Current.Position),
             SyntaxTokenType.True => new BooleanLiteralNode(true, enumerator.Current.Position),
             SyntaxTokenType.False => new BooleanLiteralNode(false, enumerator.Current.Position),
@@ -626,7 +627,7 @@ public ref struct Parser
             enumerator.MoveNext();
             enumerator.SkipEoL();
 
-            return new NumericLiteralNode(-double.Parse(enumerator.Current.Text.Span), token.Position);
+            return new NumericLiteralNode(-ConvertTextToNumber(enumerator.Current.Text.Span), token.Position);
         }
         else
         {
@@ -992,4 +993,16 @@ public ref struct Parser
             _ => OperatorPrecedence.NonOperator,
         };
     }
+
+    static double ConvertTextToNumber(ReadOnlySpan<char> text)
+    {
+        if (text.Length > 2 && text[0] is '0' && text[1] is 'x' or 'X')
+        {
+            return HexConverter.ToDouble(text);
+        }
+        else
+        {
+            return double.Parse(text);
+        }
+    }
 }

+ 79 - 0
src/Lua/Internal/HexConverter.cs

@@ -0,0 +1,79 @@
+using System.Globalization;
+
+namespace Lua.Internal;
+
+public static class HexConverter
+{
+    public static double ToDouble(ReadOnlySpan<char> text)
+    {
+        var sign = 1;
+        if (text[0] == '-')
+        {
+            // Remove the "-0x"
+            sign = -1;
+            text = text[3..];
+        }
+        else
+        {
+            // Remove the "0x"
+            text = text[2..];
+        }
+
+        var dotIndex = text.IndexOf('.');
+        var expIndex = text.IndexOf('p');
+
+        if (dotIndex == -1 && expIndex == -1)
+        {
+            return long.Parse(text, NumberStyles.AllowHexSpecifier);
+        }
+
+        var intPart = dotIndex == -1 ? [] : text[..dotIndex];
+        var decimalPart = expIndex == -1
+            ? text.Slice(dotIndex + 1)
+            : text.Slice(dotIndex + 1, expIndex - dotIndex - 1);
+        var expPart = expIndex == -1 ? [] : text[(expIndex + 1)..];
+
+        var value = intPart.Length == 0
+            ? 0
+            : long.Parse(intPart, NumberStyles.AllowHexSpecifier);
+
+        var decimalValue = 0.0;
+        for (int i = 0; i < decimalPart.Length; i++)
+        {
+            decimalValue += ToInt(decimalPart[i]) * Math.Pow(16, -(i + 1));
+        }
+
+        double result = value + decimalValue;
+
+        if (expPart.Length > 0)
+        {
+            result *= Math.Pow(2, int.Parse(expPart));
+        }
+
+        return result * sign;
+    }
+
+    static int ToInt(char c)
+    {
+        return c switch
+        {
+            '0' => 0,
+            '1' => 1,
+            '2' => 2,
+            '3' => 3,
+            '4' => 4,
+            '5' => 5,
+            '6' => 6,
+            '7' => 7,
+            '8' => 8,
+            '9' => 9,
+            'A' or 'a' => 10,
+            'B' or 'b' => 11,
+            'C' or 'd' => 12,
+            'D' or 'e' => 13,
+            'E' or 'e' => 14,
+            'F' or 'f' => 15,
+            _ => 0
+        };
+    }
+}

+ 14 - 0
tests/Lua.Tests/HexConverterTests.cs

@@ -0,0 +1,14 @@
+using Lua.Internal;
+
+namespace Lua.Tests;
+
+public class HexConverterTests
+{
+    [TestCase("0x10", 16)]
+    [TestCase("0x0p12", 0)]
+    [TestCase("-0x1.0p-1", -0.5)]
+    public void Test_ToDouble(string text, double expected)
+    {
+        Assert.That(HexConverter.ToDouble(text), Is.EqualTo(expected));
+    }
+}