Browse Source

Fix: tonumber

AnnulusGames 1 year ago
parent
commit
86add33807
1 changed files with 71 additions and 7 deletions
  1. 71 7
      src/Lua/Standard/Basic/ToNumberFunction.cs

+ 71 - 7
src/Lua/Standard/Basic/ToNumberFunction.cs

@@ -11,11 +11,11 @@ public sealed class ToNumberFunction : LuaFunction
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument(0);
         var arg0 = context.GetArgument(0);
-        var arg1 = context.HasArgument(1)
+        int? arg1 = context.HasArgument(1)
             ? (int)context.GetArgument<double>(1)
             ? (int)context.GetArgument<double>(1)
-            : 10;
+            : null;
 
 
-        if (arg1 < 2 || arg1 > 36)
+        if (arg1 != null && (arg1 < 2 || arg1 > 36))
         {
         {
             throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'tonumber' (base out of range)");
             throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'tonumber' (base out of range)");
         }
         }
@@ -26,7 +26,7 @@ public sealed class ToNumberFunction : LuaFunction
         }
         }
         else if (arg0.TryRead<string>(out var str))
         else if (arg0.TryRead<string>(out var str))
         {
         {
-            if (arg1 == 10 || arg1 == 16)
+            if (arg1 == null)
             {
             {
                 if (arg0.TryRead<double>(out var result))
                 if (arg0.TryRead<double>(out var result))
                 {
                 {
@@ -37,9 +37,9 @@ public sealed class ToNumberFunction : LuaFunction
                     buffer.Span[0] = LuaValue.Nil;
                     buffer.Span[0] = LuaValue.Nil;
                 }
                 }
             }
             }
-            else if (arg0 == 10)
+            else if (arg1 == 10)
             {
             {
-                if (double.TryParse(str, out double result))
+                if (double.TryParse(str, out var result))
                 {
                 {
                     buffer.Span[0] = result;
                     buffer.Span[0] = result;
                 }
                 }
@@ -52,7 +52,14 @@ public sealed class ToNumberFunction : LuaFunction
             {
             {
                 try
                 try
                 {
                 {
-                    buffer.Span[0] = Convert.ToInt64(str, arg1);
+                    // if the base is not 10, str cannot contain a minus sign
+                    var span = str.AsSpan().Trim();
+                    var sign = span[0] == '-' ? -1 : 1;
+                    if (sign == -1)
+                    {
+                        span = span[1..];
+                    }
+                    buffer.Span[0] = sign * StringToDouble(span, arg1.Value);
                 }
                 }
                 catch (FormatException)
                 catch (FormatException)
                 {
                 {
@@ -67,4 +74,61 @@ public sealed class ToNumberFunction : LuaFunction
 
 
         return new(1);
         return new(1);
     }
     }
+
+    static double StringToDouble(ReadOnlySpan<char> text, int toBase)
+    {
+        var value = 0.0;
+        for (int i = 0; i < text.Length; i++)
+        {
+            var v = text[i] 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 'C' => 12,
+                'd' or 'D' => 13,
+                'e' or 'E' => 14,
+                'f' or 'F' => 15,
+                'g' or 'G' => 16,
+                'h' or 'H' => 17,
+                'i' or 'I' => 18,
+                'j' or 'J' => 19,
+                'k' or 'K' => 20,
+                'l' or 'L' => 21,
+                'm' or 'M' => 22,
+                'n' or 'N' => 23,
+                'o' or 'O' => 24,
+                'p' or 'P' => 25,
+                'q' or 'Q' => 26,
+                'r' or 'R' => 27,
+                's' or 'S' => 28,
+                't' or 'T' => 29,
+                'u' or 'U' => 30,
+                'v' or 'V' => 31,
+                'w' or 'W' => 32,
+                'x' or 'X' => 33,
+                'y' or 'Y' => 34,
+                'z' or 'Z' => 35,
+                _ => 0,
+            };
+
+            if (v >= toBase)
+            {
+                throw new FormatException();
+            }
+
+            value += v * Math.Pow(toBase, text.Length - i - 1);
+        }
+
+        return value;
+    }
 }
 }