Browse Source

Merge pull request #32 from AnnulusGames/redesign-luafunction

Redesign `LuaFunction`
Annulus Games 1 year ago
parent
commit
ce5b41df3f
100 changed files with 1676 additions and 2412 deletions
  1. 39 7
      README.md
  2. 36 4
      README_JA.md
  3. 5 10
      sandbox/Benchmark/AddBenchmark.cs
  4. 0 21
      sandbox/ConsoleApp1/Program.cs
  5. 2 2
      src/Lua/Exceptions.cs
  6. 0 29
      src/Lua/LuaFunction.Create.cs
  7. 8 12
      src/Lua/LuaFunction.cs
  8. 1 0
      src/Lua/LuaFunctionExecutionContext.cs
  9. 1 7
      src/Lua/Runtime/Closure.cs
  10. 8 0
      src/Lua/Runtime/LuaStack.cs
  11. 3 2
      src/Lua/Runtime/LuaVirtualMachine.cs
  12. 0 26
      src/Lua/Standard/Basic/AssertFunction.cs
  13. 0 13
      src/Lua/Standard/Basic/CollectGarbageFunction.cs
  14. 0 22
      src/Lua/Standard/Basic/DoFileFunction.cs
  15. 0 16
      src/Lua/Standard/Basic/ErrorFunction.cs
  16. 0 37
      src/Lua/Standard/Basic/GetMetatableFunction.cs
  17. 0 55
      src/Lua/Standard/Basic/IPairsFunction.cs
  18. 0 35
      src/Lua/Standard/Basic/LoadFileFunction.cs
  19. 0 51
      src/Lua/Standard/Basic/LoadFunction.cs
  20. 0 25
      src/Lua/Standard/Basic/NextFunction.cs
  21. 0 37
      src/Lua/Standard/Basic/PCallFunction.cs
  22. 0 30
      src/Lua/Standard/Basic/PairsFunction.cs
  23. 0 24
      src/Lua/Standard/Basic/PrintFunction.cs
  24. 0 17
      src/Lua/Standard/Basic/RawEqualFunction.cs
  25. 0 17
      src/Lua/Standard/Basic/RawGetFunction.cs
  26. 0 28
      src/Lua/Standard/Basic/RawLenFunction.cs
  27. 0 18
      src/Lua/Standard/Basic/RawSetFunction.cs
  28. 0 45
      src/Lua/Standard/Basic/SelectFunction.cs
  29. 0 37
      src/Lua/Standard/Basic/SetMetatableFunction.cs
  30. 0 145
      src/Lua/Standard/Basic/ToNumberFunction.cs
  31. 0 13
      src/Lua/Standard/Basic/ToStringFunction.cs
  32. 0 27
      src/Lua/Standard/Basic/TypeFunction.cs
  33. 0 52
      src/Lua/Standard/Basic/XPCallFunction.cs
  34. 625 0
      src/Lua/Standard/BasicLibrary.cs
  35. 0 31
      src/Lua/Standard/Bitwise/ArshiftFunction.cs
  36. 0 33
      src/Lua/Standard/Bitwise/BandFunction.cs
  37. 0 17
      src/Lua/Standard/Bitwise/BnotFunction.cs
  38. 0 33
      src/Lua/Standard/Bitwise/BorFunction.cs
  39. 0 33
      src/Lua/Standard/Bitwise/BtestFunction.cs
  40. 0 33
      src/Lua/Standard/Bitwise/BxorFunction.cs
  41. 0 38
      src/Lua/Standard/Bitwise/ExtractFunction.cs
  42. 0 31
      src/Lua/Standard/Bitwise/LRotateFunciton.cs
  43. 0 35
      src/Lua/Standard/Bitwise/LShiftFunction.cs
  44. 0 31
      src/Lua/Standard/Bitwise/RRotateFunction.cs
  45. 0 35
      src/Lua/Standard/Bitwise/RShiftFunction.cs
  46. 0 43
      src/Lua/Standard/Bitwise/ReplaceFunction.cs
  47. 342 0
      src/Lua/Standard/BitwiseLibrary.cs
  48. 88 0
      src/Lua/Standard/CoroutineLibrary.cs
  49. 0 15
      src/Lua/Standard/Coroutines/CoroutineCreateFunction.cs
  50. 0 14
      src/Lua/Standard/Coroutines/CoroutineResumeFunction.cs
  51. 0 16
      src/Lua/Standard/Coroutines/CoroutineRunningFunction.cs
  52. 0 22
      src/Lua/Standard/Coroutines/CoroutineStatusFunction.cs
  53. 0 48
      src/Lua/Standard/Coroutines/CoroutineWrapFunction.cs
  54. 0 13
      src/Lua/Standard/Coroutines/CoroutineYieldFunction.cs
  55. 255 0
      src/Lua/Standard/FileHandle.cs
  56. 0 28
      src/Lua/Standard/IO/CloseFunction.cs
  57. 0 26
      src/Lua/Standard/IO/FileFlushFunction.cs
  58. 0 133
      src/Lua/Standard/IO/FileHandle.cs
  59. 0 29
      src/Lua/Standard/IO/FileLinesFunction.cs
  60. 0 18
      src/Lua/Standard/IO/FileReadFunction.cs
  61. 0 41
      src/Lua/Standard/IO/FileSeekFunction.cs
  62. 0 26
      src/Lua/Standard/IO/FileSetVBufFunction.cs
  63. 0 14
      src/Lua/Standard/IO/FileWriteFunction.cs
  64. 0 26
      src/Lua/Standard/IO/FlushFunction.cs
  65. 0 34
      src/Lua/Standard/IO/InputFunction.cs
  66. 0 45
      src/Lua/Standard/IO/LinesFunction.cs
  67. 0 18
      src/Lua/Standard/IO/OpenFunction.cs
  68. 0 34
      src/Lua/Standard/IO/OutputFunction.cs
  69. 0 14
      src/Lua/Standard/IO/ReadFunction.cs
  70. 0 23
      src/Lua/Standard/IO/TypeFunction.cs
  71. 0 14
      src/Lua/Standard/IO/WriteFunction.cs
  72. 210 0
      src/Lua/Standard/IOLibrary.cs
  73. 4 4
      src/Lua/Standard/Internal/Bit32Helper.cs
  74. 48 48
      src/Lua/Standard/Internal/DateTimeHelper.cs
  75. 1 2
      src/Lua/Standard/Internal/IOHelper.cs
  76. 0 16
      src/Lua/Standard/Mathematics/AbsFunction.cs
  77. 0 16
      src/Lua/Standard/Mathematics/AcosFuncion.cs
  78. 0 16
      src/Lua/Standard/Mathematics/AsinFuncion.cs
  79. 0 18
      src/Lua/Standard/Mathematics/Atan2Function.cs
  80. 0 16
      src/Lua/Standard/Mathematics/AtanFuncion.cs
  81. 0 16
      src/Lua/Standard/Mathematics/CeilFuncion.cs
  82. 0 16
      src/Lua/Standard/Mathematics/CosFuncion.cs
  83. 0 16
      src/Lua/Standard/Mathematics/CoshFuncion.cs
  84. 0 16
      src/Lua/Standard/Mathematics/DegFunction.cs
  85. 0 16
      src/Lua/Standard/Mathematics/ExpFuncion.cs
  86. 0 16
      src/Lua/Standard/Mathematics/FloorFuncion.cs
  87. 0 17
      src/Lua/Standard/Mathematics/FmodFunction.cs
  88. 0 19
      src/Lua/Standard/Mathematics/FrexpFunction.cs
  89. 0 18
      src/Lua/Standard/Mathematics/LdexpFunction.cs
  90. 0 26
      src/Lua/Standard/Mathematics/LogFunction.cs
  91. 0 22
      src/Lua/Standard/Mathematics/MaxFunction.cs
  92. 0 22
      src/Lua/Standard/Mathematics/MinFunction.cs
  93. 0 18
      src/Lua/Standard/Mathematics/ModfFunction.cs
  94. 0 18
      src/Lua/Standard/Mathematics/PowFunction.cs
  95. 0 16
      src/Lua/Standard/Mathematics/RadFunction.cs
  96. 0 33
      src/Lua/Standard/Mathematics/RandomFunction.cs
  97. 0 16
      src/Lua/Standard/Mathematics/RandomSeedFunction.cs
  98. 0 16
      src/Lua/Standard/Mathematics/SinFuncion.cs
  99. 0 15
      src/Lua/Standard/Mathematics/SinhFuncion.cs
  100. 0 16
      src/Lua/Standard/Mathematics/SqrtFunction.cs

+ 39 - 7
README.md

@@ -199,20 +199,28 @@ Console.WriteLine(funcResults[0]);
 
 ### Calling C# Functions from Lua
 
-You can create a `LuaFunction` from a lambda expression using `LuaFunction.Create()`.
+It is possible to create a `LuaFunction` from a lambda expression.
 
 ```cs
-// Add a function to the global environment
-state.Environment["add"] = LuaFunction.Create((args, ct) =>
+// Add the function to the global environment
+state.Environment["add"] = new LuaFunction((context, buffer, ct) =>
 {
-    return new[] { args[0].Read<double>() + args[1].Read<double>() };
+    // Get the arguments using context.GetArgument<T>()
+    var arg0 = context.GetArgument<double>(0);
+    var arg1 = context.GetArgument<double>(1);
+
+    // Write the return value to the buffer
+    buffer.Span[0] = arg0 + arg1;
+
+    // Return the number of values
+    return new(1);
 });
 
-// Execute Lua script
+// Execute a Lua script
 var results = await state.DoFileAsync("cs2lua.lua");
 
 // 3
-Console.WriteLine(results[0]);
+Console.WriteLine(results[i]);
 ```
 
 ```lua
@@ -221,7 +229,31 @@ Console.WriteLine(results[0]);
 return add(1, 2)
 ```
 
-However, the function created with `LuaFunction.Create()` causes extra allocations during invocation. For optimal performance, you should implement a class that inherits from `LuaFunction`.
+Additionally, `LuaFunction` operates asynchronously. Therefore, you can define a function that waits for an operation in Lua, such as the example below:
+
+```cs
+// Define a function that waits for the given number of seconds using Task.Delay
+state.Environment["wait"] = new LuaFunction(async (context, buffer, ct) =>
+{
+    var sec = context.GetArgument<double>(0);
+    await Task.Delay(TimeSpan.FromSeconds(sec));
+    return 0;
+});
+
+await state.DoFileAsync("sample.lua");
+```
+
+```lua
+-- sample.lua
+
+-- The incremented value will be printed to the console every second
+local i = 0
+while true do
+    i = i + 1
+    print(i)
+    wait(1.0)
+end
+```
 
 ## Coroutines
 

+ 36 - 4
README_JA.md

@@ -199,13 +199,21 @@ Console.WriteLine(funcResults[0]);
 
 ### C#の関数をLua側から呼び出す
 
-`LuaFunction.Create()`を用いてラムダ式からLuaFunctionを作成できます。
+ラムダ式からLuaFunctionを作成することが可能です。
 
 ```cs
 // グローバル環境に関数を追加
-state.Environment["add"] = LuaFunction.Create((args, ct) =>
+state.Environment["add"] = new LuaFunction((context, buffer, ct) =>
 {
-    return [args[0].Read<double>() + ]
+    // context.GetArgument<T>()で引数を取得
+    var arg0 = context.GetArgument<double>(0);
+    var arg1 = context.GetArgument<double>(1);
+
+    // バッファに戻り値を記録
+    buffer.Span[0] = arg0 + arg1;
+
+    // 戻り値の数を返す
+    return new(1);
 });
 
 // Luaスクリプトを実行
@@ -221,7 +229,31 @@ Console.WriteLine(results[i]);
 return add(1, 2)
 ```
 
-ただし、`LuaFunction.Create()`で作成された関数は呼び出し時に余計なアロケーションを発生させます。最良のパフォーマンスを得るためには`LuaFunction`を継承したクラスを実装してください。
+また、`LuaFunction`は非同期メソッドとして動作します。そのため、以下のような関数を定義することでLua側から処理の待機を行うことも可能です。
+
+```cs
+// 与えられた秒数だけTask.Delayで待機する関数を定義
+state.Environment["wait"] = new LuaFunction(async (context, buffer, ct) =>
+{
+    var sec = context.GetArgument<double>(0);
+    await Task.Delay(TimeSpan.FromSeconds(sec));
+    return 0;
+});
+
+await state.DoFileAsync("sample.lua");
+```
+
+```lua
+-- sample.lua
+
+-- 1秒ごとにインクリメントされた値がコンソールに表示される
+local i = 0
+while true do
+    i = i + 1
+    print(i)
+    wait(1.0)
+end
+```
 
 ## コルーチン
 

+ 5 - 10
sandbox/Benchmark/AddBenchmark.cs

@@ -4,15 +4,6 @@ using Lua;
 using Lua.Standard;
 using MoonSharp.Interpreter;
 
-sealed class AddFunction : Lua.LuaFunction
-{
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        buffer.Span[0] = context.GetArgument<double>(0) + context.GetArgument<double>(1);
-        return new(1);
-    }
-}
-
 [Config(typeof(BenchmarkConfig))]
 public class AddBenchmark
 {
@@ -31,7 +22,11 @@ public class AddBenchmark
         core.Setup("add.lua");
         core.LuaCSharpState.OpenStandardLibraries();
 
-        core.LuaCSharpState.Environment["add"] = new AddFunction();
+        core.LuaCSharpState.Environment["add"] = new LuaFunction("add", (context, buffer, ct) =>
+        {
+            buffer.Span[0] = context.GetArgument<double>(0) + context.GetArgument<double>(1);
+            return new(1);
+        });
         core.MoonSharpState.Globals["add"] = (Func<double, double, double>)Add;
         core.NLuaState.RegisterFunction("add", typeof(AddBenchmark).GetMethod(nameof(Add), BindingFlags.Static | BindingFlags.Public));
         core.NeoLuaEnvironment.SetValue("add", (Func<double, double, double>)Add);

+ 0 - 21
sandbox/ConsoleApp1/Program.cs

@@ -3,31 +3,10 @@ using Lua.CodeAnalysis.Compilation;
 using Lua.Runtime;
 using Lua;
 using Lua.Standard;
-using System.Reflection;
 
 var state = LuaState.Create();
 state.OpenStandardLibraries();
 
-state.Environment["wait"] = LuaFunction.Create(async (@args, ct) =>
-{
-    await Task.Delay(TimeSpan.FromSeconds(args[0].Read<double>()), ct);
-    return default!;
-});
-
-state.Environment["dumpframe"] = LuaFunction.Create((@args, ct) =>
-{
-    var thread = state.CurrentThread;
-    Console.WriteLine(thread.GetCallStackFrames()[^2]);
-    return default;
-});
-
-state.Environment["dumpstack"] = LuaFunction.Create((@args, ct) =>
-{
-    var thread = state.CurrentThread;
-    thread.GetType().GetMethod("DumpStackValues", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!.Invoke(thread, null);
-    return default;
-});
-
 try
 {
     var source = File.ReadAllText("test.lua");

+ 2 - 2
src/Lua/Exceptions.cs

@@ -73,11 +73,11 @@ public class LuaRuntimeException(Traceback traceback, string message) : LuaExcep
         throw new LuaRuntimeException(traceback, $"bad argument #{argumentId} to '{functionName}' ({expected} expected, got {actual})");
     }
 
-    public static void ThrowBadArgumentIfNumberIsNotInteger(LuaState state, LuaFunction function, int argumentId, double value)
+    public static void ThrowBadArgumentIfNumberIsNotInteger(LuaState state, string functionName, int argumentId, double value)
     {
         if (!MathEx.IsInteger(value))
         {
-            throw new LuaRuntimeException(state.GetTraceback(), $"bad argument #{argumentId} to '{function.Name}' (number has no integer representation)");
+            throw new LuaRuntimeException(state.GetTraceback(), $"bad argument #{argumentId} to '{functionName}' (number has no integer representation)");
         }
     }
 

+ 0 - 29
src/Lua/LuaFunction.Create.cs

@@ -1,29 +0,0 @@
-namespace Lua;
-
-partial class LuaFunction
-{
-    sealed class AnonymousLuaFunction(Func<LuaValue[], CancellationToken, ValueTask<LuaValue[]>> func) : LuaFunction
-    {
-        protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-        {
-            var args = context.ArgumentCount == 0 ? [] : new LuaValue[context.ArgumentCount];
-            context.Arguments.CopyTo(args);
-
-            var result = await func(args, cancellationToken);
-            if (result != null)
-            {
-                result.AsMemory().CopyTo(buffer);
-                return result.Length;
-            }
-            else
-            {
-                return 0;
-            }
-        }
-    }
-
-    public static LuaFunction Create(Func<LuaValue[], CancellationToken, ValueTask<LuaValue[]>> func)
-    {
-        return new AnonymousLuaFunction(func);
-    }
-}

+ 8 - 12
src/Lua/LuaFunction.cs

@@ -1,11 +1,15 @@
-using System.Runtime.CompilerServices;
 using Lua.Runtime;
 
 namespace Lua;
 
-public abstract partial class LuaFunction
+public class LuaFunction(string name, Func<LuaFunctionExecutionContext, Memory<LuaValue>, CancellationToken, ValueTask<int>> func)
 {
-    public virtual string Name => GetType().Name;
+    public string Name { get; } = name;
+    internal Func<LuaFunctionExecutionContext, Memory<LuaValue>, CancellationToken, ValueTask<int>> Func { get; } = func;
+
+    public LuaFunction(Func<LuaFunctionExecutionContext, Memory<LuaValue>, CancellationToken, ValueTask<int>> func) : this("anonymous", func)
+    {
+    }
 
     public async ValueTask<int> InvokeAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
@@ -22,19 +26,11 @@ public abstract partial class LuaFunction
         context.Thread.PushCallStackFrame(frame);
         try
         {
-            return await InvokeAsyncCore(context, buffer, cancellationToken);
+            return await Func(context, buffer, cancellationToken);
         }
         finally
         {
             context.Thread.PopCallStackFrame();
         }
     }
-
-    protected abstract ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken);
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal ValueTask<int> InternalInvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        return InvokeAsyncCore(context, buffer, cancellationToken);
-    }
 }

+ 1 - 0
src/Lua/LuaFunctionExecutionContext.cs

@@ -14,6 +14,7 @@ public readonly record struct LuaFunctionExecutionContext
     public SourcePosition? SourcePosition { get; init; }
     public string? RootChunkName { get; init; }
     public string? ChunkName { get; init; }
+    public object? AdditionalContext { get; init; }
 
     public ReadOnlySpan<LuaValue> Arguments
     {

+ 1 - 7
src/Lua/Runtime/Closure.cs

@@ -8,6 +8,7 @@ public sealed class Closure : LuaFunction
     FastListCore<UpValue> upValues;
 
     public Closure(LuaState state, Chunk proto, LuaTable? environment = null)
+        : base(proto.Name, (context, buffer, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, context.Thread.GetCurrentFrame(), buffer, ct))
     {
         this.proto = proto;
 
@@ -23,13 +24,6 @@ public sealed class Closure : LuaFunction
     public Chunk Proto => proto;
     public ReadOnlySpan<UpValue> UpValues => upValues.AsSpan();
 
-    public override string Name => Proto.Name;
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        return LuaVirtualMachine.ExecuteClosureAsync(context.State, this, context.Thread.GetCurrentFrame(), buffer, cancellationToken);
-    }
-
     static UpValue GetUpValueFromDescription(LuaState state, UpValue envUpValue, Chunk proto, UpValueInfo description, int depth)
     {
         if (description.IsInRegister)

+ 8 - 0
src/Lua/Runtime/LuaStack.cs

@@ -38,6 +38,14 @@ public class LuaStack(int initialSize = 256)
         top++;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void PushRange(ReadOnlySpan<LuaValue> values)
+    {
+        EnsureCapacity(top + values.Length);
+        values.CopyTo(array.AsSpan()[top..]);
+        top += values.Length;
+    }
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public LuaValue Pop()
     {

+ 3 - 2
src/Lua/Runtime/LuaVirtualMachine.cs

@@ -6,10 +6,11 @@ namespace Lua.Runtime;
 
 public static partial class LuaVirtualMachine
 {
-    internal async static ValueTask<int> ExecuteClosureAsync(LuaState state, Closure closure, CallStackFrame frame, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    internal async static ValueTask<int> ExecuteClosureAsync(LuaState state, CallStackFrame frame, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
         var thread = state.CurrentThread;
         var stack = thread.Stack;
+        var closure = (Closure)frame.Function;
         var chunk = closure.Proto;
         var rootChunk = chunk.GetRoot();
 
@@ -720,7 +721,7 @@ public static partial class LuaVirtualMachine
                             int rawResultCount;
                             try
                             {
-                                rawResultCount = await func.InternalInvokeAsyncCore(new()
+                                rawResultCount = await func.Func(new()
                                 {
                                     State = state,
                                     Thread = thread,

+ 0 - 26
src/Lua/Standard/Basic/AssertFunction.cs

@@ -1,26 +0,0 @@
-namespace Lua.Standard.Basic;
-
-public sealed class AssertFunction : LuaFunction
-{
-    public override string Name => "assert";
-    public static readonly AssertFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument(0);
-
-        if (!arg0.ToBoolean())
-        {
-            var message = "assertion failed!";
-            if (context.HasArgument(1))
-            {
-                message = context.GetArgument<string>(1);
-            }
-
-            throw new LuaAssertionException(context.State.GetTraceback(), message);
-        }
-
-        context.Arguments.CopyTo(buffer.Span);
-        return new(context.ArgumentCount);
-    }
-}

+ 0 - 13
src/Lua/Standard/Basic/CollectGarbageFunction.cs

@@ -1,13 +0,0 @@
-namespace Lua.Standard.Basic;
-
-public sealed class CollectGarbageFunction : LuaFunction
-{
-    public override string Name => "collectgarbage";
-    public static readonly CollectGarbageFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        GC.Collect();
-        return new(0);
-    }
-}

+ 0 - 22
src/Lua/Standard/Basic/DoFileFunction.cs

@@ -1,22 +0,0 @@
-using Lua.CodeAnalysis.Compilation;
-using Lua.Runtime;
-
-namespace Lua.Standard.Basic;
-
-public sealed class DoFileFunction : LuaFunction
-{
-    public override string Name => "dofile";
-    public static readonly DoFileFunction Instance = new();
-
-    protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<string>(0);
-
-        // do not use LuaState.DoFileAsync as it uses the new LuaFunctionExecutionContext
-        var text = await File.ReadAllTextAsync(arg0, cancellationToken);
-        var fileName = Path.GetFileName(arg0);
-        var chunk = LuaCompiler.Default.Compile(text, fileName);
-
-        return await new Closure(context.State, chunk).InvokeAsync(context, buffer, cancellationToken);
-    }
-}

+ 0 - 16
src/Lua/Standard/Basic/ErrorFunction.cs

@@ -1,16 +0,0 @@
-namespace Lua.Standard.Basic;
-
-public sealed class ErrorFunction : LuaFunction
-{
-    public override string Name => "error";
-    public static readonly ErrorFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var obj = context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil
-            ? "(error object is a nil value)"
-            : context.Arguments[0].ToString();
-
-        throw new LuaRuntimeException(context.State.GetTraceback(), obj!);
-    }
-}

+ 0 - 37
src/Lua/Standard/Basic/GetMetatableFunction.cs

@@ -1,37 +0,0 @@
-
-using Lua.Runtime;
-
-namespace Lua.Standard.Basic;
-
-public sealed class GetMetatableFunction : LuaFunction
-{
-    public override string Name => "getmetatable";
-    public static readonly GetMetatableFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument(0);
-
-        if (arg0.TryRead<LuaTable>(out var table))
-        {
-            if (table.Metatable == null)
-            {
-                buffer.Span[0] = LuaValue.Nil;
-            }
-            else if (table.Metatable.TryGetValue(Metamethods.Metatable, out var metatable))
-            {
-                buffer.Span[0] = metatable;
-            }
-            else
-            {
-                buffer.Span[0] = table.Metatable;
-            }
-        }
-        else
-        {
-            buffer.Span[0] = LuaValue.Nil;
-        }
-
-        return new(1);
-    }
-}

+ 0 - 55
src/Lua/Standard/Basic/IPairsFunction.cs

@@ -1,55 +0,0 @@
-using Lua.Runtime;
-
-namespace Lua.Standard.Basic;
-
-public sealed class IPairsFunction : LuaFunction
-{
-    public override string Name => "ipairs";
-    public static readonly IPairsFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaTable>(0);
-
-        // If table has a metamethod __ipairs, calls it with table as argument and returns the first three results from the call.
-        if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.IPairs, out var metamethod))
-        {
-            if (!metamethod.TryRead<LuaFunction>(out var function))
-            {
-                LuaRuntimeException.AttemptInvalidOperation(context.State.GetTraceback(), "call", metamethod);
-            }
-
-            return function.InvokeAsync(context, buffer, cancellationToken);
-        }
-
-        buffer.Span[0] = Iterator.Instance;
-        buffer.Span[1] = arg0;
-        buffer.Span[2] = 0;
-        return new(3);
-    }
-
-    class Iterator : LuaFunction
-    {
-        public static readonly Iterator Instance = new();
-
-        protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-        {
-            var table = context.GetArgument<LuaTable>(0);
-            var i = context.GetArgument<double>(1);
-
-            i++;
-            if (table.TryGetValue(i, out var value))
-            {
-                buffer.Span[0] = i;
-                buffer.Span[1] = value;
-            }
-            else
-            {
-                buffer.Span[0] = LuaValue.Nil;
-                buffer.Span[1] = LuaValue.Nil;
-            }
-
-            return new(2);
-        }
-    }
-}

+ 0 - 35
src/Lua/Standard/Basic/LoadFileFunction.cs

@@ -1,35 +0,0 @@
-using Lua.CodeAnalysis.Compilation;
-using Lua.Runtime;
-
-namespace Lua.Standard.Basic;
-
-public sealed class LoadFileFunction : LuaFunction
-{
-    public override string Name => "loadfile";
-    public static readonly LoadFileFunction Instance = new();
-
-    protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        // Lua-CSharp does not support binary chunks, the mode argument is ignored.
-        var arg0 = context.GetArgument<string>(0);
-        var arg2 = context.HasArgument(2)
-            ? context.GetArgument<LuaTable>(2)
-            : null;
-
-        // do not use LuaState.DoFileAsync as it uses the new LuaFunctionExecutionContext
-        try
-        {
-            var text = await File.ReadAllTextAsync(arg0, cancellationToken);
-            var fileName = Path.GetFileName(arg0);
-            var chunk = LuaCompiler.Default.Compile(text, fileName);
-            buffer.Span[0] = new Closure(context.State, chunk, arg2);
-            return 1;
-        }
-        catch (Exception ex)
-        {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            return 2;
-        }
-    }
-}

+ 0 - 51
src/Lua/Standard/Basic/LoadFunction.cs

@@ -1,51 +0,0 @@
-using Lua.CodeAnalysis.Compilation;
-using Lua.Runtime;
-
-namespace Lua.Standard.Basic;
-
-public sealed class LoadFunction : LuaFunction
-{
-    public override string Name => "load";
-    public static readonly LoadFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        // Lua-CSharp does not support binary chunks, the mode argument is ignored.
-        var arg0 = context.GetArgument(0);
-
-        var arg1 = context.HasArgument(1)
-            ? context.GetArgument<string>(1)
-            : null;
-
-        var arg3 = context.HasArgument(3)
-            ? context.GetArgument<LuaTable>(3)
-            : null;
-
-        // do not use LuaState.DoFileAsync as it uses the new LuaFunctionExecutionContext
-        try
-        {
-            if (arg0.TryRead<string>(out var str))
-            {
-                var chunk = LuaCompiler.Default.Compile(str, arg1 ?? "chunk");
-                buffer.Span[0] = new Closure(context.State, chunk, arg3);
-                return new(1);
-            }
-            else if (arg0.TryRead<LuaFunction>(out var function))
-            {
-                // TODO: 
-                throw new NotImplementedException();
-            }
-            else
-            {
-                LuaRuntimeException.BadArgument(context.State.GetTraceback(), 1, Name);
-                return default; // dummy
-            }
-        }
-        catch (Exception ex)
-        {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            return new(2);
-        }
-    }
-}

+ 0 - 25
src/Lua/Standard/Basic/NextFunction.cs

@@ -1,25 +0,0 @@
-namespace Lua.Standard.Basic;
-
-public sealed class NextFunction : LuaFunction
-{
-    public override string Name => "next";
-    public static readonly NextFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaTable>(0);
-        var arg1 = context.HasArgument(1) ? context.Arguments[1] : LuaValue.Nil;
-
-        if (arg0.TryGetNext(arg1, out var kv))
-        {
-            buffer.Span[0] = kv.Key;
-            buffer.Span[1] = kv.Value;
-            return new(2);
-        }
-        else
-        {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
-        }
-    }
-}

+ 0 - 37
src/Lua/Standard/Basic/PCallFunction.cs

@@ -1,37 +0,0 @@
-using Lua.Internal;
-
-namespace Lua.Standard.Basic;
-
-public sealed class PCallFunction : LuaFunction
-{
-    public override string Name => "pcall";
-    public static readonly PCallFunction Instance = new();
-
-    protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaFunction>(0);
-
-        try
-        {
-            using var methodBuffer = new PooledArray<LuaValue>(1024);
-
-            var resultCount = await arg0.InvokeAsync(context with
-            {
-                State = context.State,
-                ArgumentCount = context.ArgumentCount - 1,
-                FrameBase = context.FrameBase + 1,
-            }, methodBuffer.AsMemory(), cancellationToken);
-
-            buffer.Span[0] = true;
-            methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]);
-
-            return resultCount + 1;
-        }
-        catch (Exception ex)
-        {
-            buffer.Span[0] = false;
-            buffer.Span[1] = ex.Message;
-            return 2;
-        }
-    }
-}

+ 0 - 30
src/Lua/Standard/Basic/PairsFunction.cs

@@ -1,30 +0,0 @@
-using Lua.Runtime;
-
-namespace Lua.Standard.Basic;
-
-public sealed class PairsFunction : LuaFunction
-{
-    public override string Name => "pairs";
-    public static readonly PairsFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaTable>(0);
-
-        // If table has a metamethod __pairs, calls it with table as argument and returns the first three results from the call.
-        if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Pairs, out var metamethod))
-        {
-            if (!metamethod.TryRead<LuaFunction>(out var function))
-            {
-                LuaRuntimeException.AttemptInvalidOperation(context.State.GetTraceback(), "call", metamethod);
-            }
-
-            return function.InvokeAsync(context, buffer, cancellationToken);
-        }
-
-        buffer.Span[0] = NextFunction.Instance;
-        buffer.Span[1] = arg0;
-        buffer.Span[2] = LuaValue.Nil;
-        return new(3);
-    }
-}

+ 0 - 24
src/Lua/Standard/Basic/PrintFunction.cs

@@ -1,24 +0,0 @@
-using Lua.Internal;
-
-namespace Lua.Standard.Basic;
-
-public sealed class PrintFunction : LuaFunction
-{
-    public override string Name => "print";
-    public static readonly PrintFunction Instance = new();
-
-    protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        using var methodBuffer = new PooledArray<LuaValue>(1);
-
-        for (int i = 0; i < context.ArgumentCount; i++)
-        {
-            await context.Arguments[i].CallToStringAsync(context, methodBuffer.AsMemory(), cancellationToken);
-            Console.Write(methodBuffer[0]);
-            Console.Write('\t');
-        }
-
-        Console.WriteLine();
-        return 0;
-    }
-}

+ 0 - 17
src/Lua/Standard/Basic/RawEqualFunction.cs

@@ -1,17 +0,0 @@
-
-namespace Lua.Standard.Basic;
-
-public sealed class RawEqualFunction : LuaFunction
-{
-    public override string Name => "rawequal";
-    public static readonly RawEqualFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument(0);
-        var arg1 = context.GetArgument(1);
-
-        buffer.Span[0] = arg0 == arg1;
-        return new(1);
-    }
-}

+ 0 - 17
src/Lua/Standard/Basic/RawGetFunction.cs

@@ -1,17 +0,0 @@
-
-namespace Lua.Standard.Basic;
-
-public sealed class RawGetFunction : LuaFunction
-{
-    public override string Name => "rawget";
-    public static readonly RawGetFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaTable>(0);
-        var arg1 = context.GetArgument(1);
-
-        buffer.Span[0] = arg0[arg1];
-        return new(1);
-    }
-}

+ 0 - 28
src/Lua/Standard/Basic/RawLenFunction.cs

@@ -1,28 +0,0 @@
-
-namespace Lua.Standard.Basic;
-
-public sealed class RawLenFunction : LuaFunction
-{
-    public override string Name => "rawlen";
-    public static readonly RawLenFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument(0);
-
-        if (arg0.TryRead<LuaTable>(out var table))
-        {
-            buffer.Span[0] = table.ArrayLength;
-        }
-        else if (arg0.TryRead<string>(out var str))
-        {
-            buffer.Span[0] = str.Length;
-        }
-        else
-        {
-            LuaRuntimeException.BadArgument(context.State.GetTraceback(), 2, Name, [LuaValueType.String, LuaValueType.Table]);
-        }
-
-        return new(1);
-    }
-}

+ 0 - 18
src/Lua/Standard/Basic/RawSetFunction.cs

@@ -1,18 +0,0 @@
-
-namespace Lua.Standard.Basic;
-
-public sealed class RawSetFunction : LuaFunction
-{
-    public override string Name => "rawset";
-    public static readonly RawSetFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaTable>(0);
-        var arg1 = context.GetArgument(1);
-        var arg2 = context.GetArgument(2);
-
-        arg0[arg1] = arg2;
-        return new(0);
-    }
-}

+ 0 - 45
src/Lua/Standard/Basic/SelectFunction.cs

@@ -1,45 +0,0 @@
-namespace Lua.Standard.Basic;
-
-public sealed class SelectFunction : LuaFunction
-{
-    public override string Name => "select";
-    public static readonly SelectFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument(0);
-
-        if (arg0.TryRead<double>(out var d))
-        {
-            if (!MathEx.IsInteger(d))
-            {
-                throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #1 to 'select' (number has no integer representation)");
-            }
-
-            var index = (int)d;
-
-            if (Math.Abs(index) > context.ArgumentCount)
-            {
-                throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #1 to 'select' (index out of range)");
-            }
-
-            var span = index >= 0
-                ? context.Arguments[index..]
-                : context.Arguments[(context.ArgumentCount + index)..];
-
-            span.CopyTo(buffer.Span);
-
-            return new(span.Length);
-        }
-        else if (arg0.TryRead<string>(out var str) && str == "#")
-        {
-            buffer.Span[0] = context.ArgumentCount - 1;
-            return new(1);
-        }
-        else
-        {
-            LuaRuntimeException.BadArgument(context.State.GetTraceback(), 1, Name, "number", arg0.Type.ToString());
-            return default;
-        }
-    }
-}

+ 0 - 37
src/Lua/Standard/Basic/SetMetatableFunction.cs

@@ -1,37 +0,0 @@
-
-using Lua.Runtime;
-
-namespace Lua.Standard.Basic;
-
-public sealed class SetMetatableFunction : LuaFunction
-{
-    public override string Name => "setmetatable";
-    public static readonly SetMetatableFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaTable>(0);
-        var arg1 = context.GetArgument(1);
-
-        if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
-        {
-            LuaRuntimeException.BadArgument(context.State.GetTraceback(), 2, Name, [LuaValueType.Nil, LuaValueType.Table]);
-        }
-
-        if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Metatable, out _))
-        {
-            throw new LuaRuntimeException(context.State.GetTraceback(), "cannot change a protected metatable");
-        }
-        else if (arg1.Type is LuaValueType.Nil)
-        {
-            arg0.Metatable = null;
-        }
-        else
-        {
-            arg0.Metatable = arg1.Read<LuaTable>();
-        }
-
-        buffer.Span[0] = arg0;
-        return new(1);
-    }
-}

+ 0 - 145
src/Lua/Standard/Basic/ToNumberFunction.cs

@@ -1,145 +0,0 @@
-using Lua.Internal;
-
-namespace Lua.Standard.Basic;
-
-public sealed class ToNumberFunction : LuaFunction
-{
-    public override string Name => "tonumber";
-    public static readonly ToNumberFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var e = context.GetArgument(0);
-        int? toBase = context.HasArgument(1)
-            ? (int)context.GetArgument<double>(1)
-            : null;
-
-        if (toBase != null && (toBase < 2 || toBase > 36))
-        {
-            throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'tonumber' (base out of range)");
-        }
-
-        double? value = null;
-        if (e.Type is LuaValueType.Number)
-        {
-            value = e.UnsafeRead<double>();
-        }
-        else if (e.TryRead<string>(out var str))
-        {
-            if (toBase == null)
-            {
-                if (e.TryRead<double>(out var result))
-                {
-                    value = result;
-                }
-            }
-            else if (toBase == 10)
-            {
-                if (double.TryParse(str, out var result))
-                {
-                    value = result;
-                }
-            }
-            else
-            {
-                try
-                {
-                    // if the base is not 10, str cannot contain a minus sign
-                    var span = str.AsSpan().Trim();
-                    if (span.Length == 0) goto END;
-
-                    var first = span[0];
-                    var sign = first == '-' ? -1 : 1;
-                    if (first is '+' or '-')
-                    {
-                        span = span[1..];
-                    }
-                    if (span.Length == 0) goto END;
-
-                    if (toBase == 16 && span.Length > 2 && span[0] is '0' && span[1] is 'x' or 'X')
-                    {
-                        value = sign * HexConverter.ToDouble(span);
-                    }
-                    else
-                    {
-                        value = sign * StringToDouble(span, toBase.Value);
-                    }
-                }
-                catch (FormatException)
-                {
-                    goto END;
-                }
-            }
-        }
-        else
-        {
-            goto END;
-        }
-
-    END:
-        if (value != null && double.IsNaN(value.Value))
-        {
-            value = null;
-        }
-
-        buffer.Span[0] = value == null ? LuaValue.Nil : value.Value;
-        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;
-    }
-}

+ 0 - 13
src/Lua/Standard/Basic/ToStringFunction.cs

@@ -1,13 +0,0 @@
-namespace Lua.Standard.Basic;
-
-public sealed class ToStringFunction : LuaFunction
-{
-    public override string Name => "tostring";
-    public static readonly ToStringFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument(0);
-        return arg0.CallToStringAsync(context, buffer, cancellationToken);
-    }
-}

+ 0 - 27
src/Lua/Standard/Basic/TypeFunction.cs

@@ -1,27 +0,0 @@
-namespace Lua.Standard.Basic;
-
-public sealed class TypeFunction : LuaFunction
-{
-    public override string Name => "type";
-    public static readonly TypeFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument(0);
-
-        buffer.Span[0] = arg0.Type switch
-        {
-            LuaValueType.Nil => "nil",
-            LuaValueType.Boolean => "boolean",
-            LuaValueType.String => "string",
-            LuaValueType.Number => "number",
-            LuaValueType.Function => "function",
-            LuaValueType.Thread => "thread",
-            LuaValueType.UserData => "userdata",
-            LuaValueType.Table => "table",
-            _ => throw new NotImplementedException(),
-        };
-
-        return new(1);
-    }
-}

+ 0 - 52
src/Lua/Standard/Basic/XPCallFunction.cs

@@ -1,52 +0,0 @@
-using Lua.Internal;
-
-namespace Lua.Standard.Basic;
-
-public sealed class XPCallFunction : LuaFunction
-{
-    public override string Name => "xpcall";
-    public static readonly XPCallFunction Instance = new();
-
-    protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaFunction>(0);
-        var arg1 = context.GetArgument<LuaFunction>(1);
-
-        using var methodBuffer = new PooledArray<LuaValue>(1024);
-        methodBuffer.AsSpan().Clear();
-
-        try
-        {
-            var resultCount = await arg0.InvokeAsync(context with
-            {
-                State = context.State,
-                ArgumentCount = context.ArgumentCount - 2,
-                FrameBase = context.FrameBase + 2,
-            }, methodBuffer.AsMemory(), cancellationToken);
-
-            buffer.Span[0] = true;
-            methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]);
-
-            return resultCount + 1;
-        }
-        catch (Exception ex)
-        {
-            methodBuffer.AsSpan().Clear();
-
-            context.State.Push(ex.Message);
-
-            // invoke error handler
-            await arg1.InvokeAsync(context with
-            {
-                State = context.State,
-                ArgumentCount = 1,
-                FrameBase = context.Thread.Stack.Count - context.ArgumentCount,
-            }, methodBuffer.AsMemory(), cancellationToken);
-
-            buffer.Span[0] = false;
-            buffer.Span[1] = ex.Message;
-
-            return 2;
-        }
-    }
-}

+ 625 - 0
src/Lua/Standard/BasicLibrary.cs

@@ -0,0 +1,625 @@
+using Lua.CodeAnalysis.Compilation;
+using Lua.Internal;
+using Lua.Runtime;
+
+namespace Lua.Standard;
+
+public static class BasicLibrary
+{
+    public static void OpenBasicLibrary(this LuaState state)
+    {
+        state.Environment["_G"] = state.Environment;
+        state.Environment["_VERSION"] = "Lua 5.2";
+        foreach (var func in Functions)
+        {
+            state.Environment[func.Name] = func;
+        }
+    }
+
+    static readonly LuaFunction[] Functions = [
+        new("assert", Assert),
+        new("collectgarbage", CollectGarbage),
+        new("dofile", DoFile),
+        new("error", Error),
+        new("getmetatable", GetMetatable),
+        new("ipairs", IPairs),
+        new("loadfile", LoadFile),
+        new("load", Load),
+        new("next", Next),
+        new("pairs", Pairs),
+        new("pcall", PCall),
+        new("print", Print),
+        new("rawequal", RawEqual),
+        new("rawget", RawGet),
+        new("rawlen", RawLen),
+        new("rawset", RawSet),
+        new("select", Select),
+        new("setmetatable", SetMetatable),
+        new("tonumber", ToNumber),
+        new("tostring", ToString),
+        new("type", Type),
+        new("xpcall", XPCall),
+    ];
+
+    static readonly LuaFunction IPairsIterator = new("iterator", (context, buffer, cancellationToken) =>
+    {
+        var table = context.GetArgument<LuaTable>(0);
+        var i = context.GetArgument<double>(1);
+
+        i++;
+        if (table.TryGetValue(i, out var value))
+        {
+            buffer.Span[0] = i;
+            buffer.Span[1] = value;
+        }
+        else
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            buffer.Span[1] = LuaValue.Nil;
+        }
+
+        return new(2);
+    });
+
+    static readonly LuaFunction PairsIterator = new("iterator", Next);
+
+    public static ValueTask<int> Assert(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument(0);
+
+        if (!arg0.ToBoolean())
+        {
+            var message = "assertion failed!";
+            if (context.HasArgument(1))
+            {
+                message = context.GetArgument<string>(1);
+            }
+
+            throw new LuaAssertionException(context.State.GetTraceback(), message);
+        }
+
+        context.Arguments.CopyTo(buffer.Span);
+        return new(context.ArgumentCount);
+    }
+
+    public static ValueTask<int> CollectGarbage(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        GC.Collect();
+        return new(0);
+    }
+
+    public static async ValueTask<int> DoFile(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<string>(0);
+
+        // do not use LuaState.DoFileAsync as it uses the newExecutionContext
+        var text = await File.ReadAllTextAsync(arg0, cancellationToken);
+        var fileName = Path.GetFileName(arg0);
+        var chunk = LuaCompiler.Default.Compile(text, fileName);
+
+        return await new Closure(context.State, chunk).InvokeAsync(context, buffer, cancellationToken);
+    }
+
+    public static ValueTask<int> Error(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var obj = context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil
+            ? "(error object is a nil value)"
+            : context.Arguments[0].ToString();
+
+        throw new LuaRuntimeException(context.State.GetTraceback(), obj!);
+    }
+
+    public static ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument(0);
+
+        if (arg0.TryRead<LuaTable>(out var table))
+        {
+            if (table.Metatable == null)
+            {
+                buffer.Span[0] = LuaValue.Nil;
+            }
+            else if (table.Metatable.TryGetValue(Metamethods.Metatable, out var metatable))
+            {
+                buffer.Span[0] = metatable;
+            }
+            else
+            {
+                buffer.Span[0] = table.Metatable;
+            }
+        }
+        else
+        {
+            buffer.Span[0] = LuaValue.Nil;
+        }
+
+        return new(1);
+    }
+
+    public static ValueTask<int> IPairs(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaTable>(0);
+
+        // If table has a metamethod __ipairs, calls it with table as argument and returns the first three results from the call.
+        if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.IPairs, out var metamethod))
+        {
+            if (!metamethod.TryRead<LuaFunction>(out var function))
+            {
+                LuaRuntimeException.AttemptInvalidOperation(context.State.GetTraceback(), "call", metamethod);
+            }
+
+            return function.InvokeAsync(context, buffer, cancellationToken);
+        }
+
+        buffer.Span[0] = IPairsIterator;
+        buffer.Span[1] = arg0;
+        buffer.Span[2] = 0;
+        return new(3);
+    }
+    
+    public static async ValueTask<int> LoadFile(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        // Lua-CSharp does not support binary chunks, the mode argument is ignored.
+        var arg0 = context.GetArgument<string>(0);
+        var arg2 = context.HasArgument(2)
+            ? context.GetArgument<LuaTable>(2)
+            : null;
+
+        // do not use LuaState.DoFileAsync as it uses the newExecutionContext
+        try
+        {
+            var text = await File.ReadAllTextAsync(arg0, cancellationToken);
+            var fileName = Path.GetFileName(arg0);
+            var chunk = LuaCompiler.Default.Compile(text, fileName);
+            buffer.Span[0] = new Closure(context.State, chunk, arg2);
+            return 1;
+        }
+        catch (Exception ex)
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            buffer.Span[1] = ex.Message;
+            return 2;
+        }
+    }
+
+    public static ValueTask<int> Load(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        // Lua-CSharp does not support binary chunks, the mode argument is ignored.
+        var arg0 = context.GetArgument(0);
+
+        var arg1 = context.HasArgument(1)
+            ? context.GetArgument<string>(1)
+            : null;
+
+        var arg3 = context.HasArgument(3)
+            ? context.GetArgument<LuaTable>(3)
+            : null;
+
+        // do not use LuaState.DoFileAsync as it uses the newExecutionContext
+        try
+        {
+            if (arg0.TryRead<string>(out var str))
+            {
+                var chunk = LuaCompiler.Default.Compile(str, arg1 ?? "chunk");
+                buffer.Span[0] = new Closure(context.State, chunk, arg3);
+                return new(1);
+            }
+            else if (arg0.TryRead<LuaFunction>(out var function))
+            {
+                // TODO: 
+                throw new NotImplementedException();
+            }
+            else
+            {
+                LuaRuntimeException.BadArgument(context.State.GetTraceback(), 1, "load");
+                return default; // dummy
+            }
+        }
+        catch (Exception ex)
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            buffer.Span[1] = ex.Message;
+            return new(2);
+        }
+    }
+
+    public static ValueTask<int> Next(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.HasArgument(1) ? context.Arguments[1] : LuaValue.Nil;
+
+        if (arg0.TryGetNext(arg1, out var kv))
+        {
+            buffer.Span[0] = kv.Key;
+            buffer.Span[1] = kv.Value;
+            return new(2);
+        }
+        else
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            return new(1);
+        }
+    }
+    
+    public static ValueTask<int> Pairs(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaTable>(0);
+
+        // If table has a metamethod __pairs, calls it with table as argument and returns the first three results from the call.
+        if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Pairs, out var metamethod))
+        {
+            if (!metamethod.TryRead<LuaFunction>(out var function))
+            {
+                LuaRuntimeException.AttemptInvalidOperation(context.State.GetTraceback(), "call", metamethod);
+            }
+
+            return function.InvokeAsync(context, buffer, cancellationToken);
+        }
+
+        buffer.Span[0] = PairsIterator;
+        buffer.Span[1] = arg0;
+        buffer.Span[2] = LuaValue.Nil;
+        return new(3);
+    }
+
+    public static async ValueTask<int> PCall(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaFunction>(0);
+
+        try
+        {
+            using var methodBuffer = new PooledArray<LuaValue>(1024);
+
+            var resultCount = await arg0.InvokeAsync(context with
+            {
+                State = context.State,
+                ArgumentCount = context.ArgumentCount - 1,
+                FrameBase = context.FrameBase + 1,
+            }, methodBuffer.AsMemory(), cancellationToken);
+
+            buffer.Span[0] = true;
+            methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]);
+
+            return resultCount + 1;
+        }
+        catch (Exception ex)
+        {
+            buffer.Span[0] = false;
+            buffer.Span[1] = ex.Message;
+            return 2;
+        }
+    }
+
+    public static async ValueTask<int> Print(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        using var methodBuffer = new PooledArray<LuaValue>(1);
+
+        for (int i = 0; i < context.ArgumentCount; i++)
+        {
+            await context.Arguments[i].CallToStringAsync(context, methodBuffer.AsMemory(), cancellationToken);
+            Console.Write(methodBuffer[0]);
+            Console.Write('\t');
+        }
+
+        Console.WriteLine();
+        return 0;
+    }
+
+    public static ValueTask<int> RawEqual(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument(0);
+        var arg1 = context.GetArgument(1);
+
+        buffer.Span[0] = arg0 == arg1;
+        return new(1);
+    }
+
+    public static ValueTask<int> RawGet(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.GetArgument(1);
+
+        buffer.Span[0] = arg0[arg1];
+        return new(1);
+    }
+
+    public static ValueTask<int> RawLen(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument(0);
+
+        if (arg0.TryRead<LuaTable>(out var table))
+        {
+            buffer.Span[0] = table.ArrayLength;
+        }
+        else if (arg0.TryRead<string>(out var str))
+        {
+            buffer.Span[0] = str.Length;
+        }
+        else
+        {
+            LuaRuntimeException.BadArgument(context.State.GetTraceback(), 2, "rawlen", [LuaValueType.String, LuaValueType.Table]);
+        }
+
+        return new(1);
+    }
+
+    public static ValueTask<int> RawSet(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.GetArgument(1);
+        var arg2 = context.GetArgument(2);
+
+        arg0[arg1] = arg2;
+        return new(0);
+    }
+    
+    public static ValueTask<int> Select(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument(0);
+
+        if (arg0.TryRead<double>(out var d))
+        {
+            if (!MathEx.IsInteger(d))
+            {
+                throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #1 to 'select' (number has no integer representation)");
+            }
+
+            var index = (int)d;
+
+            if (Math.Abs(index) > context.ArgumentCount)
+            {
+                throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #1 to 'select' (index out of range)");
+            }
+
+            var span = index >= 0
+                ? context.Arguments[index..]
+                : context.Arguments[(context.ArgumentCount + index)..];
+
+            span.CopyTo(buffer.Span);
+
+            return new(span.Length);
+        }
+        else if (arg0.TryRead<string>(out var str) && str == "#")
+        {
+            buffer.Span[0] = context.ArgumentCount - 1;
+            return new(1);
+        }
+        else
+        {
+            LuaRuntimeException.BadArgument(context.State.GetTraceback(), 1, "select", "number", arg0.Type.ToString());
+            return default;
+        }
+    }
+    
+    public static ValueTask<int> SetMetatable(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.GetArgument(1);
+
+        if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
+        {
+            LuaRuntimeException.BadArgument(context.State.GetTraceback(), 2, "setmetatable", [LuaValueType.Nil, LuaValueType.Table]);
+        }
+
+        if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Metatable, out _))
+        {
+            throw new LuaRuntimeException(context.State.GetTraceback(), "cannot change a protected metatable");
+        }
+        else if (arg1.Type is LuaValueType.Nil)
+        {
+            arg0.Metatable = null;
+        }
+        else
+        {
+            arg0.Metatable = arg1.Read<LuaTable>();
+        }
+
+        buffer.Span[0] = arg0;
+        return new(1);
+    }
+
+    public static ValueTask<int> ToNumber(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var e = context.GetArgument(0);
+        int? toBase = context.HasArgument(1)
+            ? (int)context.GetArgument<double>(1)
+            : null;
+
+        if (toBase != null && (toBase < 2 || toBase > 36))
+        {
+            throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'tonumber' (base out of range)");
+        }
+
+        double? value = null;
+        if (e.Type is LuaValueType.Number)
+        {
+            value = e.UnsafeRead<double>();
+        }
+        else if (e.TryRead<string>(out var str))
+        {
+            if (toBase == null)
+            {
+                if (e.TryRead<double>(out var result))
+                {
+                    value = result;
+                }
+            }
+            else if (toBase == 10)
+            {
+                if (double.TryParse(str, out var result))
+                {
+                    value = result;
+                }
+            }
+            else
+            {
+                try
+                {
+                    // if the base is not 10, str cannot contain a minus sign
+                    var span = str.AsSpan().Trim();
+                    if (span.Length == 0) goto END;
+
+                    var first = span[0];
+                    var sign = first == '-' ? -1 : 1;
+                    if (first is '+' or '-')
+                    {
+                        span = span[1..];
+                    }
+                    if (span.Length == 0) goto END;
+
+                    if (toBase == 16 && span.Length > 2 && span[0] is '0' && span[1] is 'x' or 'X')
+                    {
+                        value = sign * HexConverter.ToDouble(span);
+                    }
+                    else
+                    {
+                        value = sign * StringToDouble(span, toBase.Value);
+                    }
+                }
+                catch (FormatException)
+                {
+                    goto END;
+                }
+            }
+        }
+        else
+        {
+            goto END;
+        }
+
+    END:
+        if (value != null && double.IsNaN(value.Value))
+        {
+            value = null;
+        }
+
+        buffer.Span[0] = value == null ? LuaValue.Nil : value.Value;
+        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;
+    }
+
+    public static ValueTask<int> ToString(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument(0);
+        return arg0.CallToStringAsync(context, buffer, cancellationToken);
+    }
+    
+    public static ValueTask<int> Type(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument(0);
+
+        buffer.Span[0] = arg0.Type switch
+        {
+            LuaValueType.Nil => "nil",
+            LuaValueType.Boolean => "boolean",
+            LuaValueType.String => "string",
+            LuaValueType.Number => "number",
+            LuaValueType.Function => "function",
+            LuaValueType.Thread => "thread",
+            LuaValueType.UserData => "userdata",
+            LuaValueType.Table => "table",
+            _ => throw new NotImplementedException(),
+        };
+
+        return new(1);
+    }
+
+    public static async ValueTask<int> XPCall(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaFunction>(0);
+        var arg1 = context.GetArgument<LuaFunction>(1);
+
+        using var methodBuffer = new PooledArray<LuaValue>(1024);
+        methodBuffer.AsSpan().Clear();
+
+        try
+        {
+            var resultCount = await arg0.InvokeAsync(context with
+            {
+                State = context.State,
+                ArgumentCount = context.ArgumentCount - 2,
+                FrameBase = context.FrameBase + 2,
+            }, methodBuffer.AsMemory(), cancellationToken);
+
+            buffer.Span[0] = true;
+            methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]);
+
+            return resultCount + 1;
+        }
+        catch (Exception ex)
+        {
+            methodBuffer.AsSpan().Clear();
+
+            context.State.Push(ex.Message);
+
+            // invoke error handler
+            await arg1.InvokeAsync(context with
+            {
+                State = context.State,
+                ArgumentCount = 1,
+                FrameBase = context.Thread.Stack.Count - context.ArgumentCount,
+            }, methodBuffer.AsMemory(), cancellationToken);
+
+            buffer.Span[0] = false;
+            buffer.Span[1] = ex.Message;
+
+            return 2;
+        }
+    }
+}

+ 0 - 31
src/Lua/Standard/Bitwise/ArshiftFunction.cs

@@ -1,31 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class ArshiftFunction : LuaFunction
-{
-    public override string Name => "arshift";
-    public static readonly ArshiftFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var x = context.GetArgument<double>(0);
-        var disp = context.GetArgument<double>(1);
-
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, disp);
-
-        var v = Bit32Helper.ToInt32(x);
-        var a = (int)disp;
-
-        if (a < 0)
-        {
-            v <<= -a;
-        }
-        else
-        {
-            v >>= a;
-        }
-
-        buffer.Span[0] = (uint)v;
-        return new(1);
-    }
-}

+ 0 - 33
src/Lua/Standard/Bitwise/BandFunction.cs

@@ -1,33 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class BandFunction : LuaFunction
-{
-    public override string Name => "band";
-    public static readonly BandFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        if (context.ArgumentCount == 0)
-        {
-            buffer.Span[0] = uint.MaxValue;
-            return new(1);
-        }
-
-        var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, arg0);
-
-        var value = Bit32Helper.ToUInt32(arg0);
-
-        for (int i = 1; i < context.ArgumentCount; i++)
-        {
-            var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1 + i, arg);
-
-            var v = Bit32Helper.ToUInt32(arg);
-            value &= v;
-        }
-
-        buffer.Span[0] = value;
-        return new(1);
-    }
-}

+ 0 - 17
src/Lua/Standard/Bitwise/BnotFunction.cs

@@ -1,17 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class BnotFunction : LuaFunction
-{
-    public override string Name => "bnot";
-    public static readonly BnotFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, arg0);
-
-        var value = Bit32Helper.ToUInt32(arg0);
-        buffer.Span[0] = ~value;
-        return new(1);
-    }
-}

+ 0 - 33
src/Lua/Standard/Bitwise/BorFunction.cs

@@ -1,33 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class BorFunction : LuaFunction
-{
-    public override string Name => "bor";
-    public static readonly BorFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        if (context.ArgumentCount == 0)
-        {
-            buffer.Span[0] = 0;
-            return new(1);
-        }
-
-        var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, arg0);
-
-        var value = Bit32Helper.ToUInt32(arg0);
-
-        for (int i = 1; i < context.ArgumentCount; i++)
-        {
-            var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1 + i, arg);
-
-            var v = Bit32Helper.ToUInt32(arg);
-            value |= v;
-        }
-
-        buffer.Span[0] = value;
-        return new(1);
-    }
-}

+ 0 - 33
src/Lua/Standard/Bitwise/BtestFunction.cs

@@ -1,33 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class BtestFunction : LuaFunction
-{
-    public override string Name => "btest";
-    public static readonly BtestFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        if (context.ArgumentCount == 0)
-        {
-            buffer.Span[0] = true;
-            return new(1);
-        }
-
-        var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, arg0);
-
-        var value = Bit32Helper.ToUInt32(arg0);
-
-        for (int i = 1; i < context.ArgumentCount; i++)
-        {
-            var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1 + i, arg);
-
-            var v = Bit32Helper.ToUInt32(arg);
-            value &= v;
-        }
-
-        buffer.Span[0] = value != 0;
-        return new(1);
-    }
-}

+ 0 - 33
src/Lua/Standard/Bitwise/BxorFunction.cs

@@ -1,33 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class BxorFunction : LuaFunction
-{
-    public override string Name => "bxor";
-    public static readonly BxorFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        if (context.ArgumentCount == 0)
-        {
-            buffer.Span[0] = 0;
-            return new(1);
-        }
-
-        var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, arg0);
-
-        var value = Bit32Helper.ToUInt32(arg0);
-
-        for (int i = 1; i < context.ArgumentCount; i++)
-        {
-            var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1 + i, arg);
-
-            var v = Bit32Helper.ToUInt32(arg);
-            value ^= v;
-        }
-
-        buffer.Span[0] = value;
-        return new(1);
-    }
-}

+ 0 - 38
src/Lua/Standard/Bitwise/ExtractFunction.cs

@@ -1,38 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class ExtractFunction : LuaFunction
-{
-    public override string Name => "extract";
-    public static readonly ExtractFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        var arg1 = context.GetArgument<double>(1);
-        var arg2 = context.HasArgument(2)
-            ? context.GetArgument<double>(2)
-            : 1;
-
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, arg0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, arg1);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 3, arg2);
-
-        var n = Bit32Helper.ToUInt32(arg0);
-        var field = (int)arg1;
-        var width = (int)arg2;
-
-        Bit32Helper.ValidateFieldAndWidth(context.State, this, 2, field, width);
-        
-        if (field == 0 && width == 32)
-        {
-            buffer.Span[0] = n;
-        }
-        else
-        {
-            var mask = (uint)((1 << width) - 1);
-            buffer.Span[0] = (n >> field) & mask;
-        }
-
-        return new(1);
-    }
-}

+ 0 - 31
src/Lua/Standard/Bitwise/LRotateFunciton.cs

@@ -1,31 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class LRotateFunction : LuaFunction
-{
-    public override string Name => "lrotate";
-    public static readonly LRotateFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var x = context.GetArgument<double>(0);
-        var disp = context.GetArgument<double>(1);
-
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, disp);
-
-        var v = Bit32Helper.ToUInt32(x);
-        var a = ((int)disp) % 32;
-
-        if (a < 0)
-        {
-            v = (v >> (-a)) | (v << (32 + a));
-        }
-        else
-        {
-            v = (v << a) | (v >> (32 - a));
-        }
-
-        buffer.Span[0] = v;
-        return new(1);
-    }
-}

+ 0 - 35
src/Lua/Standard/Bitwise/LShiftFunction.cs

@@ -1,35 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class LShiftFunction : LuaFunction
-{
-    public override string Name => "lshift";
-    public static readonly LShiftFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var x = context.GetArgument<double>(0);
-        var disp = context.GetArgument<double>(1);
-
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, disp);
-
-        var v = Bit32Helper.ToUInt32(x);
-        var a = (int)disp;
-
-        if (Math.Abs(a) >= 32)
-        {
-            v = 0;
-        }
-        else if (a < 0)
-        {
-            v >>= -a;
-        }
-        else
-        {
-            v <<= a;
-        }
-
-        buffer.Span[0] = v;
-        return new(1);
-    }
-}

+ 0 - 31
src/Lua/Standard/Bitwise/RRotateFunction.cs

@@ -1,31 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class RRotateFunction : LuaFunction
-{
-    public override string Name => "rrotate";
-    public static readonly RRotateFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var x = context.GetArgument<double>(0);
-        var disp = context.GetArgument<double>(1);
-
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, disp);
-
-        var v = Bit32Helper.ToUInt32(x);
-        var a = ((int)disp) % 32;
-
-        if (a < 0)
-        {
-            v = (v << (-a)) | (v >> (32 + a));
-        }
-        else
-        {
-            v = (v >> a) | (v << (32 - a));
-        }
-
-        buffer.Span[0] = v;
-        return new(1);
-    }
-}

+ 0 - 35
src/Lua/Standard/Bitwise/RShiftFunction.cs

@@ -1,35 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class RShiftFunction : LuaFunction
-{
-    public override string Name => "rshift";
-    public static readonly RShiftFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var x = context.GetArgument<double>(0);
-        var disp = context.GetArgument<double>(1);
-
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, disp);
-
-        var v = Bit32Helper.ToUInt32(x);
-        var a = (int)disp;
-
-        if (Math.Abs(a) >= 32)
-        {
-            v = 0;
-        }
-        else if (a < 0)
-        {
-            v <<= -a;
-        }
-        else
-        {
-            v >>= a;
-        }
-
-        buffer.Span[0] = v;
-        return new(1);
-    }
-}

+ 0 - 43
src/Lua/Standard/Bitwise/ReplaceFunction.cs

@@ -1,43 +0,0 @@
-namespace Lua.Standard.Bitwise;
-
-public sealed class ReplaceFunction : LuaFunction
-{
-    public override string Name => "replace";
-    public static readonly ReplaceFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        var arg1 = context.GetArgument<double>(1);
-        var arg2 = context.GetArgument<double>(2);
-        var arg3 = context.HasArgument(3)
-            ? context.GetArgument<double>(3)
-            : 1;
-
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, arg0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, arg1);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 3, arg2);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 4, arg3);
-
-        var n = Bit32Helper.ToUInt32(arg0);
-        var v = Bit32Helper.ToUInt32(arg1);
-        var field = (int)arg2;
-        var width = (int)arg3;
-
-        Bit32Helper.ValidateFieldAndWidth(context.State, this, 2, field, width);
-        uint mask;
-        if (width == 32)
-        {
-            mask = 0xFFFFFFFF;
-        }
-        else
-        {
-            mask = (uint)((1 << width) - 1);
-        }
-
-        v = v & mask;
-        n = (n & ~(mask << field)) | (v << field);
-        buffer.Span[0] = n;
-        return new(1);
-    }
-}

+ 342 - 0
src/Lua/Standard/BitwiseLibrary.cs

@@ -0,0 +1,342 @@
+using Lua.Standard.Internal;
+
+namespace Lua.Standard;
+
+public static class BitwiseLibrary
+{
+    public static void OpenBitwiseLibrary(this LuaState state)
+    {
+        var bit32 = new LuaTable(0, Functions.Length);
+        foreach (var func in Functions)
+        {
+            bit32[func.Name] = func;
+        }
+
+        state.Environment["bit32"] = bit32;
+        state.LoadedModules["bit32"] = bit32;
+    }
+
+    static readonly LuaFunction[] Functions = [
+        new ("arshift", ArShift),
+        new ("band", BAnd),
+        new ("bnot", BNot),
+        new ("bor", BOr),
+        new ("btest", BTest),
+        new ("bxor", BXor),
+        new ("extract", Extract),
+        new ("lrotate", LRotate),
+        new ("lshift", LShift),
+        new ("replace", Replace),
+        new ("rrotate", RRotate),
+        new ("rshift", RShift),
+    ];
+
+    public static ValueTask<int> ArShift(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var x = context.GetArgument<double>(0);
+        var disp = context.GetArgument<double>(1);
+
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "arshift", 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "arshift", 2, disp);
+
+        var v = Bit32Helper.ToInt32(x);
+        var a = (int)disp;
+
+        if (a < 0)
+        {
+            v <<= -a;
+        }
+        else
+        {
+            v >>= a;
+        }
+
+        buffer.Span[0] = (uint)v;
+        return new(1);
+    }
+
+    public static ValueTask<int> BAnd(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        if (context.ArgumentCount == 0)
+        {
+            buffer.Span[0] = uint.MaxValue;
+            return new(1);
+        }
+
+        var arg0 = context.GetArgument<double>(0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "band", 1, arg0);
+
+        var value = Bit32Helper.ToUInt32(arg0);
+
+        for (int i = 1; i < context.ArgumentCount; i++)
+        {
+            var arg = context.GetArgument<double>(i);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "band", 1 + i, arg);
+
+            var v = Bit32Helper.ToUInt32(arg);
+            value &= v;
+        }
+
+        buffer.Span[0] = value;
+        return new(1);
+    }
+
+    public static ValueTask<int> BNot(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<double>(0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "bnot", 1, arg0);
+
+        var value = Bit32Helper.ToUInt32(arg0);
+        buffer.Span[0] = ~value;
+        return new(1);
+    }
+
+    public static ValueTask<int> BOr(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        if (context.ArgumentCount == 0)
+        {
+            buffer.Span[0] = 0;
+            return new(1);
+        }
+
+        var arg0 = context.GetArgument<double>(0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "bor", 1, arg0);
+
+        var value = Bit32Helper.ToUInt32(arg0);
+
+        for (int i = 1; i < context.ArgumentCount; i++)
+        {
+            var arg = context.GetArgument<double>(i);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "bor", 1 + i, arg);
+
+            var v = Bit32Helper.ToUInt32(arg);
+            value |= v;
+        }
+
+        buffer.Span[0] = value;
+        return new(1);
+    }
+
+    public static ValueTask<int> BTest(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        if (context.ArgumentCount == 0)
+        {
+            buffer.Span[0] = true;
+            return new(1);
+        }
+
+        var arg0 = context.GetArgument<double>(0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "btest", 1, arg0);
+
+        var value = Bit32Helper.ToUInt32(arg0);
+
+        for (int i = 1; i < context.ArgumentCount; i++)
+        {
+            var arg = context.GetArgument<double>(i);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "btest", 1 + i, arg);
+
+            var v = Bit32Helper.ToUInt32(arg);
+            value &= v;
+        }
+
+        buffer.Span[0] = value != 0;
+        return new(1);
+    }
+
+    public static ValueTask<int> BXor(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        if (context.ArgumentCount == 0)
+        {
+            buffer.Span[0] = 0;
+            return new(1);
+        }
+
+        var arg0 = context.GetArgument<double>(0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "bxor", 1, arg0);
+
+        var value = Bit32Helper.ToUInt32(arg0);
+
+        for (int i = 1; i < context.ArgumentCount; i++)
+        {
+            var arg = context.GetArgument<double>(i);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "bxor", 1 + i, arg);
+
+            var v = Bit32Helper.ToUInt32(arg);
+            value ^= v;
+        }
+
+        buffer.Span[0] = value;
+        return new(1);
+    }
+
+    public static ValueTask<int> Extract(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<double>(0);
+        var arg1 = context.GetArgument<double>(1);
+        var arg2 = context.HasArgument(2)
+            ? context.GetArgument<double>(2)
+            : 1;
+
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "extract", 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "extract", 2, arg1);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "extract", 3, arg2);
+
+        var n = Bit32Helper.ToUInt32(arg0);
+        var field = (int)arg1;
+        var width = (int)arg2;
+
+        Bit32Helper.ValidateFieldAndWidth(context.State, "extract", 2, field, width);
+
+        if (field == 0 && width == 32)
+        {
+            buffer.Span[0] = n;
+        }
+        else
+        {
+            var mask = (uint)((1 << width) - 1);
+            buffer.Span[0] = (n >> field) & mask;
+        }
+
+        return new(1);
+    }
+
+    public static ValueTask<int> LRotate(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var x = context.GetArgument<double>(0);
+        var disp = context.GetArgument<double>(1);
+
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "lrotate", 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "lrotate", 2, disp);
+
+        var v = Bit32Helper.ToUInt32(x);
+        var a = ((int)disp) % 32;
+
+        if (a < 0)
+        {
+            v = (v >> (-a)) | (v << (32 + a));
+        }
+        else
+        {
+            v = (v << a) | (v >> (32 - a));
+        }
+
+        buffer.Span[0] = v;
+        return new(1);
+    }
+
+    public static ValueTask<int> LShift(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var x = context.GetArgument<double>(0);
+        var disp = context.GetArgument<double>(1);
+
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "lshift", 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "lshift", 2, disp);
+
+        var v = Bit32Helper.ToUInt32(x);
+        var a = (int)disp;
+
+        if (Math.Abs(a) >= 32)
+        {
+            v = 0;
+        }
+        else if (a < 0)
+        {
+            v >>= -a;
+        }
+        else
+        {
+            v <<= a;
+        }
+
+        buffer.Span[0] = v;
+        return new(1);
+    }
+
+    public static ValueTask<int> Replace(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<double>(0);
+        var arg1 = context.GetArgument<double>(1);
+        var arg2 = context.GetArgument<double>(2);
+        var arg3 = context.HasArgument(3)
+            ? context.GetArgument<double>(3)
+            : 1;
+
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "replace", 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "replace", 2, arg1);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "replace", 3, arg2);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "replace", 4, arg3);
+
+        var n = Bit32Helper.ToUInt32(arg0);
+        var v = Bit32Helper.ToUInt32(arg1);
+        var field = (int)arg2;
+        var width = (int)arg3;
+
+        Bit32Helper.ValidateFieldAndWidth(context.State, "replace", 2, field, width);
+        uint mask;
+        if (width == 32)
+        {
+            mask = 0xFFFFFFFF;
+        }
+        else
+        {
+            mask = (uint)((1 << width) - 1);
+        }
+
+        v = v & mask;
+        n = (n & ~(mask << field)) | (v << field);
+        buffer.Span[0] = n;
+        return new(1);
+    }
+
+    public static ValueTask<int> RRotate(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var x = context.GetArgument<double>(0);
+        var disp = context.GetArgument<double>(1);
+
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "rrotate", 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "rrotate", 2, disp);
+
+        var v = Bit32Helper.ToUInt32(x);
+        var a = ((int)disp) % 32;
+
+        if (a < 0)
+        {
+            v = (v << (-a)) | (v >> (32 + a));
+        }
+        else
+        {
+            v = (v >> a) | (v << (32 - a));
+        }
+
+        buffer.Span[0] = v;
+        return new(1);
+    }
+
+    public static ValueTask<int> RShift(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var x = context.GetArgument<double>(0);
+        var disp = context.GetArgument<double>(1);
+
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "rshift", 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "rshift", 2, disp);
+
+        var v = Bit32Helper.ToUInt32(x);
+        var a = (int)disp;
+
+        if (Math.Abs(a) >= 32)
+        {
+            v = 0;
+        }
+        else if (a < 0)
+        {
+            v <<= -a;
+        }
+        else
+        {
+            v >>= a;
+        }
+
+        buffer.Span[0] = v;
+        return new(1);
+    }
+}

+ 88 - 0
src/Lua/Standard/CoroutineLibrary.cs

@@ -0,0 +1,88 @@
+namespace Lua.Standard;
+
+public static class CoroutineLibrary
+{
+    public static void OpenCoroutineLibrary(this LuaState state)
+    {
+        var coroutine = new LuaTable(0, Functions.Length);
+        foreach (var func in Functions)
+        {
+            coroutine[func.Name] = func;
+        }
+
+        state.Environment["coroutine"] = coroutine;
+    }
+
+    static readonly LuaFunction[] Functions = [
+        new("create", Create),
+        new("resume", Resume),
+        new("running", Running),
+        new("wrap", Wrap),
+        new("yield", Yield),
+    ];
+
+    public static ValueTask<int> Create(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaFunction>(0);
+        buffer.Span[0] = new LuaCoroutine(arg0, true);
+        return new(1);
+    }
+
+    public static ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var thread = context.GetArgument<LuaThread>(0);
+        return thread.ResumeAsync(context, buffer, cancellationToken);
+    }
+
+    public static ValueTask<int> Running(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        buffer.Span[0] = context.Thread;
+        buffer.Span[1] = context.Thread == context.State.MainThread;
+        return new(2);
+    }
+
+    public static ValueTask<int> Status(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var thread = context.GetArgument<LuaThread>(0);
+        buffer.Span[0] = thread.GetStatus() switch
+        {
+            LuaThreadStatus.Normal => "normal",
+            LuaThreadStatus.Suspended => "suspended",
+            LuaThreadStatus.Running => "running",
+            LuaThreadStatus.Dead => "dead",
+            _ => "",
+        };
+        return new(1);
+    }
+
+    public static ValueTask<int> Wrap(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<LuaFunction>(0);
+        var thread = new LuaCoroutine(arg0, false);
+
+        buffer.Span[0] = new LuaFunction("wrap", async (context, buffer, cancellationToken) =>
+        {
+            var stack = context.Thread.Stack;
+            var frameBase = stack.Count;
+
+            stack.Push(thread);
+            stack.PushRange(context.Arguments);
+
+            var resultCount = await thread.ResumeAsync(context with
+            {
+                ArgumentCount = context.ArgumentCount + 1,
+                FrameBase = frameBase,
+            }, buffer, cancellationToken);
+
+            buffer.Span[1..].CopyTo(buffer.Span[0..]);
+            return resultCount - 1;
+        });
+
+        return new(1);
+    }
+
+    public static ValueTask<int> Yield(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        return context.Thread.YieldAsync(context, buffer, cancellationToken);
+    }
+}

+ 0 - 15
src/Lua/Standard/Coroutines/CoroutineCreateFunction.cs

@@ -1,15 +0,0 @@
-
-namespace Lua.Standard.Coroutines;
-
-public sealed class CoroutineCreateFunction : LuaFunction
-{
-    public static readonly CoroutineCreateFunction Instance = new();
-    public override string Name => "create";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaFunction>(0);
-        buffer.Span[0] = new LuaCoroutine(arg0, true);
-        return new(1);
-    }
-}

+ 0 - 14
src/Lua/Standard/Coroutines/CoroutineResumeFunction.cs

@@ -1,14 +0,0 @@
-
-namespace Lua.Standard.Coroutines;
-
-public sealed class CoroutineResumeFunction : LuaFunction
-{
-    public static readonly CoroutineResumeFunction Instance = new();
-    public override string Name => "resume";
-
-    protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var thread = context.GetArgument<LuaThread>(0);
-        return await thread.ResumeAsync(context, buffer, cancellationToken);
-    }
-}

+ 0 - 16
src/Lua/Standard/Coroutines/CoroutineRunningFunction.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Coroutines;
-
-public sealed class CoroutineRunningFunction : LuaFunction
-{
-    public static readonly CoroutineRunningFunction Instance = new();
-    public override string Name => "running";
-
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        buffer.Span[0] = context.Thread;
-        buffer.Span[1] = context.Thread == context.State.MainThread;
-        return new(2);
-    }
-}

+ 0 - 22
src/Lua/Standard/Coroutines/CoroutineStatusFunction.cs

@@ -1,22 +0,0 @@
-
-namespace Lua.Standard.Coroutines;
-
-public sealed class CoroutineStatusFunction : LuaFunction
-{
-    public static readonly CoroutineStatusFunction Instance = new();
-    public override string Name => "status";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var thread = context.GetArgument<LuaThread>(0);
-        buffer.Span[0] = thread.GetStatus() switch
-        {
-            LuaThreadStatus.Normal => "normal",
-            LuaThreadStatus.Suspended => "suspended",
-            LuaThreadStatus.Running => "running",
-            LuaThreadStatus.Dead => "dead",
-            _ => throw new NotImplementedException(),
-        };
-        return new(1);
-    }
-}

+ 0 - 48
src/Lua/Standard/Coroutines/CoroutineWrapFunction.cs

@@ -1,48 +0,0 @@
-
-using Lua.Runtime;
-
-namespace Lua.Standard.Coroutines;
-
-public sealed class CoroutineWrapFunction : LuaFunction
-{
-    public static readonly CoroutineWrapFunction Instance = new();
-    public override string Name => "wrap";
-
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<LuaFunction>(0);
-        var thread = new LuaCoroutine(arg0, false);
-        buffer.Span[0] = new Wrapper(thread);
-        return new(1);
-    }
-
-    class Wrapper(LuaThread targetThread) : LuaFunction
-    {
-        protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-        {
-            var stack = context.Thread.Stack;
-            var frameBase = stack.Count;
-
-            stack.Push(targetThread);
-            PushArguments(stack, context.Arguments);
-
-            var resultCount = await targetThread.ResumeAsync(context with
-            {
-                ArgumentCount = context.ArgumentCount + 1,
-                FrameBase = frameBase,
-            }, buffer, cancellationToken);
-
-            buffer.Span[1..].CopyTo(buffer.Span[0..]);
-            return resultCount - 1;
-        }
-
-        static void PushArguments(LuaStack stack, ReadOnlySpan<LuaValue> arguments)
-        {
-            foreach (var arg in arguments)
-            {
-                stack.Push(arg);
-            }
-        }
-    }
-}

+ 0 - 13
src/Lua/Standard/Coroutines/CoroutineYieldFunction.cs

@@ -1,13 +0,0 @@
-
-namespace Lua.Standard.Coroutines;
-
-public sealed class CoroutineYieldFunction : LuaFunction
-{
-    public static readonly CoroutineYieldFunction Instance = new();
-    public override string Name => "yield";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        return context.Thread.YieldAsync(context, buffer, cancellationToken);
-    }
-}

+ 255 - 0
src/Lua/Standard/FileHandle.cs

@@ -0,0 +1,255 @@
+using Lua.Runtime;
+using Lua.Standard.Internal;
+
+namespace Lua.Standard;
+
+// TODO: optimize (remove StreamReader/Writer)
+
+public class FileHandle : LuaUserData
+{
+    public static readonly LuaFunction IndexMetamethod = new("index", (context, buffer, ct) =>
+    {
+        context.GetArgument<FileHandle>(0);
+        var key = context.GetArgument(1);
+
+        if (key.TryRead<string>(out var name))
+        {
+            buffer.Span[0] = name switch
+            {
+                "close" => CloseFunction!,
+                "flush" => FlushFunction!,
+                "lines" => LinesFunction!,
+                "read" => ReadFunction!,
+                "seek" => SeekFunction!,
+                "setvbuf" => SetVBufFunction!,
+                "write" => WriteFunction!,
+                _ => LuaValue.Nil,
+            };
+        }
+        else
+        {
+            buffer.Span[0] = LuaValue.Nil;
+        }
+
+        return new(1);
+    });
+
+    Stream stream;
+    StreamWriter? writer;
+    StreamReader? reader;
+    bool isClosed;
+
+    public bool IsClosed => Volatile.Read(ref isClosed);
+
+    static readonly LuaTable fileHandleMetatable;
+
+    static FileHandle()
+    {
+        fileHandleMetatable = new LuaTable();
+        fileHandleMetatable[Metamethods.Index] = IndexMetamethod;
+    }
+
+    public FileHandle(Stream stream)
+    {
+        this.stream = stream;
+        if (stream.CanRead) reader = new StreamReader(stream);
+        if (stream.CanWrite) writer = new StreamWriter(stream);
+        Metatable = fileHandleMetatable;
+    }
+
+    public string? ReadLine()
+    {
+        return reader!.ReadLine();
+    }
+
+    public string ReadToEnd()
+    {
+        return reader!.ReadToEnd();
+    }
+
+    public int ReadByte()
+    {
+        return stream.ReadByte();
+    }
+
+    public void Write(ReadOnlySpan<char> buffer)
+    {
+        writer!.Write(buffer);
+    }
+
+    public long Seek(string whence, long offset)
+    {
+        if (whence != null)
+        {
+            switch (whence)
+            {
+                case "set":
+                    stream.Seek(offset, SeekOrigin.Begin);
+                    break;
+                case "cur":
+                    stream.Seek(offset, SeekOrigin.Current);
+                    break;
+                case "end":
+                    stream.Seek(offset, SeekOrigin.End);
+                    break;
+                default:
+                    throw new ArgumentException($"Invalid option '{whence}'");
+            }
+        }
+
+        return stream.Position;
+    }
+
+    public void Flush()
+    {
+        writer!.Flush();
+    }
+
+    public void SetVBuf(string mode, int size)
+    {
+        // Ignore size parameter
+
+        if (writer != null)
+        {
+            writer.AutoFlush = mode is "no" or "line";
+        }
+    }
+
+    public void Close()
+    {
+        if (isClosed) throw new ObjectDisposedException(nameof(FileHandle));
+        Volatile.Write(ref isClosed, true);
+
+        if (reader != null)
+        {
+            reader.Dispose();
+        }
+        else
+        {
+            stream.Close();
+        }
+    }
+
+    static readonly LuaFunction CloseFunction = new("close", (context, buffer, cancellationToken) =>
+    {
+        var file = context.GetArgument<FileHandle>(0);
+
+        try
+        {
+            file.Close();
+            buffer.Span[0] = true;
+            return new(1);
+        }
+        catch (IOException ex)
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            buffer.Span[1] = ex.Message;
+            buffer.Span[2] = ex.HResult;
+            return new(3);
+        }
+    });
+
+    static readonly LuaFunction FlushFunction = new("flush", (context, buffer, cancellationToken) =>
+    {
+        var file = context.GetArgument<FileHandle>(0);
+
+        try
+        {
+            file.Flush();
+            buffer.Span[0] = true;
+            return new(1);
+        }
+        catch (IOException ex)
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            buffer.Span[1] = ex.Message;
+            buffer.Span[2] = ex.HResult;
+            return new(3);
+        }
+    });
+
+    static readonly LuaFunction LinesFunction = new("lines", (context, buffer, cancellationToken) =>
+    {
+        var file = context.GetArgument<FileHandle>(0);
+        var format = context.HasArgument(1)
+            ? context.Arguments[1]
+            : "*l";
+
+        LuaValue[] formats = [format];
+
+        buffer.Span[0] = new LuaFunction("iterator", (context, buffer, cancellationToken) =>
+        {
+            var resultCount = IOHelper.Read(context.State, file, "lines", 0, formats, buffer, true);
+            return new(resultCount);
+        });
+
+        return new(1);
+    });
+
+    static readonly LuaFunction ReadFunction = new("read", (context, buffer, cancellationToken) =>
+    {
+        var file = context.GetArgument<FileHandle>(0);
+        var resultCount = IOHelper.Read(context.State, file, "read", 1, context.Arguments[1..], buffer, false);
+        return new(resultCount);
+    });
+
+    static readonly LuaFunction SeekFunction = new("seek", (context, buffer, cancellationToken) =>
+    {
+        var file = context.GetArgument<FileHandle>(0);
+        var whence = context.HasArgument(1)
+            ? context.GetArgument<string>(1)
+            : "cur";
+        var offset = context.HasArgument(2)
+            ? context.GetArgument<double>(2)
+            : 0;
+
+        if (whence is not ("set" or "cur" or "end"))
+        {
+            throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #2 to 'seek' (invalid option '{whence}')");
+        }
+
+        if (!MathEx.IsInteger(offset))
+        {
+            throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #3 to 'seek' (number has no integer representation)");
+        }
+
+        try
+        {
+            buffer.Span[0] = file.Seek(whence, (long)offset);
+            return new(1);
+        }
+        catch (IOException ex)
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            buffer.Span[1] = ex.Message;
+            buffer.Span[2] = ex.HResult;
+            return new(3);
+        }
+    });
+
+    static readonly LuaFunction SetVBufFunction = new("setvbuf", (context, buffer, cancellationToken) =>
+    {
+        var file = context.GetArgument<FileHandle>(0);
+        var mode = context.GetArgument<string>(1);
+        var size = context.HasArgument(2)
+            ? context.GetArgument<double>(2)
+            : -1;
+
+        if (!MathEx.IsInteger(size))
+        {
+            throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #3 to 'setvbuf' (number has no integer representation)");
+        }
+
+        file.SetVBuf(mode, (int)size);
+
+        buffer.Span[0] = true;
+        return new(1);
+    });
+
+    static readonly LuaFunction WriteFunction = new("write", (context, buffer, cancellationToken) =>
+    {
+        var file = context.GetArgument<FileHandle>(0);
+        var resultCount = IOHelper.Write(file, "write", context, buffer);
+        return new(resultCount);
+    });
+}

+ 0 - 28
src/Lua/Standard/IO/CloseFunction.cs

@@ -1,28 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class CloseFunction : LuaFunction
-{
-    public override string Name => "close";
-    public static readonly CloseFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.HasArgument(0)
-            ? context.GetArgument<FileHandle>(0)
-            : context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
-
-        try
-        {
-            file.Close();
-            buffer.Span[0] = true;
-            return new(1);
-        }
-        catch (IOException ex)
-        {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
-        }
-    }
-}

+ 0 - 26
src/Lua/Standard/IO/FileFlushFunction.cs

@@ -1,26 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class FileFlushFunction : LuaFunction
-{
-    public override string Name => "flush";
-    public static readonly FileFlushFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.GetArgument<FileHandle>(0);
-
-        try
-        {
-            file.Flush();
-            buffer.Span[0] = true;
-            return new(1);
-        }
-        catch (IOException ex)
-        {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
-        }
-    }
-}

+ 0 - 133
src/Lua/Standard/IO/FileHandle.cs

@@ -1,133 +0,0 @@
-using Lua.Runtime;
-
-namespace Lua.Standard.IO;
-
-// TODO: optimize (remove StreamReader/Writer)
-
-public class FileHandle : LuaUserData
-{
-    class IndexMetamethod : LuaFunction
-    {
-        protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-        {
-            context.GetArgument<FileHandle>(0);
-            var key = context.GetArgument(1);
-
-            if (key.TryRead<string>(out var name))
-            {
-                buffer.Span[0] = name switch
-                {
-                    "write" => FileWriteFunction.Instance,
-                    "read" => FileReadFunction.Instance,
-                    "lines" => FileLinesFunction.Instance,
-                    "flush" => FileFlushFunction.Instance,
-                    "setvbuf" => FileSetVBufFunction.Instance,
-                    "close" => CloseFunction.Instance,
-                    _ => LuaValue.Nil,
-                };
-            }
-            else
-            {
-                buffer.Span[0] = LuaValue.Nil;
-            }
-
-            return new(1);
-        }
-    }
-
-    Stream stream;
-    StreamWriter? writer;
-    StreamReader? reader;
-    bool isClosed;
-
-    public bool IsClosed => Volatile.Read(ref isClosed);
-
-    static readonly LuaTable fileHandleMetatable;
-
-    static FileHandle()
-    {
-        fileHandleMetatable = new LuaTable();
-        fileHandleMetatable[Metamethods.Index] = new IndexMetamethod();
-    }
-
-    public FileHandle(Stream stream)
-    {
-        this.stream = stream;
-        if (stream.CanRead) reader = new StreamReader(stream);
-        if (stream.CanWrite) writer = new StreamWriter(stream);
-        Metatable = fileHandleMetatable;
-    }
-
-    public string? ReadLine()
-    {
-        return reader!.ReadLine();
-    }
-
-    public string ReadToEnd()
-    {
-        return reader!.ReadToEnd();
-    }
-
-    public int ReadByte()
-    {
-        return stream.ReadByte();
-    }
-
-    public void Write(ReadOnlySpan<char> buffer)
-    {
-        writer!.Write(buffer);
-    }
-
-    public long Seek(string whence, long offset)
-    {
-        if (whence != null)
-        {
-            switch (whence)
-            {
-                case "set":
-                    stream.Seek(offset, SeekOrigin.Begin);
-                    break;
-                case "cur":
-                    stream.Seek(offset, SeekOrigin.Current);
-                    break;
-                case "end":
-                    stream.Seek(offset, SeekOrigin.End);
-                    break;
-                default:
-                    throw new ArgumentException($"Invalid option '{whence}'");
-            }
-        }
-
-        return stream.Position;
-    }
-
-    public void Flush()
-    {
-        writer!.Flush();
-    }
-
-    public void SetVBuf(string mode, int size)
-    {
-        // Ignore size parameter
-
-        if (writer != null)
-        {
-            writer.AutoFlush = mode is "no" or "line";
-        }
-    }
-
-    public void Close()
-    {
-        if (isClosed) throw new ObjectDisposedException(nameof(FileHandle));
-        Volatile.Write(ref isClosed, true);
-
-        if (reader != null)
-        {
-            reader.Dispose();
-        }
-        else
-        {
-            stream.Close();
-        }
-    }
-}

+ 0 - 29
src/Lua/Standard/IO/FileLinesFunction.cs

@@ -1,29 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class FileLinesFunction : LuaFunction
-{
-    public override string Name => "lines";
-    public static readonly FileLinesFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<FileHandle>(0);
-        var arg1 = context.HasArgument(1)
-            ? context.Arguments[1]
-            : "*l";
-
-        buffer.Span[0] = new Iterator(arg0, arg1);
-        return new(1);
-    }
-
-    class Iterator(FileHandle file, LuaValue format) : LuaFunction
-    {
-        readonly LuaValue[] formats = [format];
-
-        protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-        {
-            var resultCount = IOHelper.Read(context.State, file, Name, 0, formats, buffer, true);
-            return new(resultCount);
-        }
-    }
-}

+ 0 - 18
src/Lua/Standard/IO/FileReadFunction.cs

@@ -1,18 +0,0 @@
-using System.Buffers.Text;
-using System.Text;
-using Lua.Internal;
-
-namespace Lua.Standard.IO;
-
-public sealed class FileReadFunction : LuaFunction
-{
-    public override string Name => "read";
-    public static readonly FileReadFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.GetArgument<FileHandle>(0);
-        var resultCount = IOHelper.Read(context.State, file, Name, 1, context.Arguments[1..], buffer, false);
-        return new(resultCount);
-    }
-}

+ 0 - 41
src/Lua/Standard/IO/FileSeekFunction.cs

@@ -1,41 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class FileSeekFunction : LuaFunction
-{
-    public override string Name => "seek";
-    public static readonly FileSeekFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.GetArgument<FileHandle>(0);
-        var whence = context.HasArgument(1)
-            ? context.GetArgument<string>(1)
-            : "cur";
-        var offset = context.HasArgument(2)
-            ? context.GetArgument<double>(2)
-            : 0;
-
-        if (whence is not ("set" or "cur" or "end"))
-        {
-            throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #2 to 'seek' (invalid option '{whence}')");
-        }
-
-        if (!MathEx.IsInteger(offset))
-        {
-            throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #3 to 'seek' (number has no integer representation)");
-        }
-
-        try
-        {
-            buffer.Span[0] = file.Seek(whence, (long)offset);
-            return new(1);
-        }
-        catch (IOException ex)
-        {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
-        }
-    }
-}

+ 0 - 26
src/Lua/Standard/IO/FileSetVBufFunction.cs

@@ -1,26 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class FileSetVBufFunction : LuaFunction
-{
-    public override string Name => "setvbuf";
-    public static readonly FileSetVBufFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.GetArgument<FileHandle>(0);
-        var mode = context.GetArgument<string>(1);
-        var size = context.HasArgument(2)
-            ? context.GetArgument<double>(2)
-            : -1;
-
-        if (!MathEx.IsInteger(size))
-        {
-            throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #3 to 'setvbuf' (number has no integer representation)");
-        }
-
-        file.SetVBuf(mode, (int)size);
-
-        buffer.Span[0] = true;
-        return new(1);
-    }
-}

+ 0 - 14
src/Lua/Standard/IO/FileWriteFunction.cs

@@ -1,14 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class FileWriteFunction : LuaFunction
-{
-    public override string Name => "write";
-    public static readonly FileWriteFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.GetArgument<FileHandle>(0);
-        var resultCount = IOHelper.Write(file, Name, context, buffer);
-        return new(resultCount);
-    }
-}

+ 0 - 26
src/Lua/Standard/IO/FlushFunction.cs

@@ -1,26 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class FlushFunction : LuaFunction
-{
-    public override string Name => "flush";
-    public static readonly FlushFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
-
-        try
-        {
-            file.Flush();
-            buffer.Span[0] = true;
-            return new(1);
-        }
-        catch (IOException ex)
-        {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
-        }
-    }
-}

+ 0 - 34
src/Lua/Standard/IO/InputFunction.cs

@@ -1,34 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class InputFunction : LuaFunction
-{
-    public override string Name => "input";
-    public static readonly InputFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var io = context.State.Environment["io"].Read<LuaTable>();
-
-        if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
-        {
-            buffer.Span[0] = io["stdio"];
-            return new(1);
-        }
-
-        var arg = context.Arguments[0];
-        if (arg.TryRead<FileHandle>(out var file))
-        {
-            io["stdio"] = file;
-            buffer.Span[0] = file;
-            return new(1);
-        }
-        else
-        {
-            var stream = File.Open(arg.ToString()!, FileMode.Open, FileAccess.ReadWrite);
-            var handle = new FileHandle(stream);
-            io["stdio"] = handle;
-            buffer.Span[0] = handle;
-            return new(1);
-        }
-    }
-}

+ 0 - 45
src/Lua/Standard/IO/LinesFunction.cs

@@ -1,45 +0,0 @@
-using Lua.Internal;
-
-namespace Lua.Standard.IO;
-
-public sealed class LinesFunction : LuaFunction
-{
-    public override string Name => "lines";
-    public static readonly LinesFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        if (context.ArgumentCount == 0)
-        {
-            var file = context.State.Environment["io"].Read<LuaTable>()["stdio"].Read<FileHandle>();
-            buffer.Span[0] = new Iterator(file, []);
-            return new(1);
-        }
-        else
-        {
-            var fileName = context.GetArgument<string>(0);
-
-            using var methodBuffer = new PooledArray<LuaValue>(32);
-            IOHelper.Open(context.State, fileName, "r", methodBuffer.AsMemory(), true);
-
-            var file = methodBuffer[0].Read<FileHandle>();
-            buffer.Span[0] = new Iterator(file, context.Arguments[1..]);
-            return new(1);
-        }
-    }
-
-    class Iterator(FileHandle file, ReadOnlySpan<LuaValue> formats) : LuaFunction
-    {
-        readonly LuaValue[] formats = formats.ToArray();
-
-        protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-        {
-            var resultCount = IOHelper.Read(context.State, file, Name, 0, formats, buffer, true);
-            if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil)
-            {
-                file.Close();
-            }
-            return new(resultCount);
-        }
-    }
-}

+ 0 - 18
src/Lua/Standard/IO/OpenFunction.cs

@@ -1,18 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class OpenFunction : LuaFunction
-{
-    public override string Name => "open";
-    public static readonly OpenFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var fileName = context.GetArgument<string>(0);
-        var mode = context.HasArgument(1)
-            ? context.GetArgument<string>(1)
-            : "r";
-
-        var resultCount = IOHelper.Open(context.State, fileName, mode, buffer, false);
-        return new(resultCount);
-    }
-}

+ 0 - 34
src/Lua/Standard/IO/OutputFunction.cs

@@ -1,34 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class OutputFunction : LuaFunction
-{
-    public override string Name => "output";
-    public static readonly OutputFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var io = context.State.Environment["io"].Read<LuaTable>();
-
-        if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
-        {
-            buffer.Span[0] = io["stdout"];
-            return new(1);
-        }
-
-        var arg = context.Arguments[0];
-        if (arg.TryRead<FileHandle>(out var file))
-        {
-            io["stdout"] = file;
-            buffer.Span[0] = file;
-            return new(1);
-        }
-        else
-        {
-            var stream = File.Open(arg.ToString()!, FileMode.Open, FileAccess.ReadWrite);
-            var handle = new FileHandle(stream);
-            io["stdout"] = handle;
-            buffer.Span[0] = handle;
-            return new(1);
-        }
-    }
-}

+ 0 - 14
src/Lua/Standard/IO/ReadFunction.cs

@@ -1,14 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class ReadFunction : LuaFunction
-{
-    public override string Name => "read";
-    public static readonly ReadFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.State.Environment["io"].Read<LuaTable>()["stdio"].Read<FileHandle>();
-        var resultCount = IOHelper.Read(context.State, file, Name, 0, context.Arguments, buffer, false);
-        return new(resultCount);
-    }
-}

+ 0 - 23
src/Lua/Standard/IO/TypeFunction.cs

@@ -1,23 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class TypeFunction : LuaFunction
-{
-    public override string Name => "type";
-    public static readonly TypeFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument(0);
-
-        if (arg0.TryRead<FileHandle>(out var file))
-        {
-            buffer.Span[0] = file.IsClosed ? "closed file" : "file";
-        }
-        else
-        {
-            buffer.Span[0] = LuaValue.Nil;
-        }
-
-        return new(1);
-    }
-}

+ 0 - 14
src/Lua/Standard/IO/WriteFunction.cs

@@ -1,14 +0,0 @@
-namespace Lua.Standard.IO;
-
-public sealed class WriteFunction : LuaFunction
-{
-    public override string Name => "write";
-    public static readonly WriteFunction Instance = new();
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var file = context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
-        var resultCount = IOHelper.Write(file, Name, context, buffer);
-        return new(resultCount);
-    }
-}

+ 210 - 0
src/Lua/Standard/IOLibrary.cs

@@ -0,0 +1,210 @@
+using Lua.Internal;
+using Lua.Standard.Internal;
+
+namespace Lua.Standard;
+
+public static class IOLibrary
+{
+    public static void OpenIOLibrary(this LuaState state)
+    {
+        var io = new LuaTable(0, Functions.Length);
+        foreach (var func in Functions)
+        {
+            io[func.Name] = func;
+        }
+
+        io["stdio"] = new FileHandle(Console.OpenStandardInput());
+        io["stdout"] = new FileHandle(Console.OpenStandardOutput());
+        io["stderr"] = new FileHandle(Console.OpenStandardError());
+
+        state.Environment["io"] = io;
+        state.LoadedModules["io"] = io;
+    }
+
+    static readonly LuaFunction[] Functions = [
+        new("close", Close),
+        new("flush", Flush),
+        new("input", Input),
+        new("lines", Lines),
+        new("open", Open),
+        new("output", Output),
+        new("read", Read),
+        new("type", Type),
+        new("write", Write),
+    ];
+
+    public static ValueTask<int> Close(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var file = context.HasArgument(0)
+            ? context.GetArgument<FileHandle>(0)
+            : context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
+
+        try
+        {
+            file.Close();
+            buffer.Span[0] = true;
+            return new(1);
+        }
+        catch (IOException ex)
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            buffer.Span[1] = ex.Message;
+            buffer.Span[2] = ex.HResult;
+            return new(3);
+        }
+    }
+
+    public static ValueTask<int> Flush(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var file = context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
+
+        try
+        {
+            file.Flush();
+            buffer.Span[0] = true;
+            return new(1);
+        }
+        catch (IOException ex)
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            buffer.Span[1] = ex.Message;
+            buffer.Span[2] = ex.HResult;
+            return new(3);
+        }
+    }
+
+    public static ValueTask<int> Input(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var io = context.State.Environment["io"].Read<LuaTable>();
+
+        if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
+        {
+            buffer.Span[0] = io["stdio"];
+            return new(1);
+        }
+
+        var arg = context.Arguments[0];
+        if (arg.TryRead<FileHandle>(out var file))
+        {
+            io["stdio"] = file;
+            buffer.Span[0] = file;
+            return new(1);
+        }
+        else
+        {
+            var stream = File.Open(arg.ToString()!, FileMode.Open, FileAccess.ReadWrite);
+            var handle = new FileHandle(stream);
+            io["stdio"] = handle;
+            buffer.Span[0] = handle;
+            return new(1);
+        }
+    }
+
+    public static ValueTask<int> Lines(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        if (context.ArgumentCount == 0)
+        {
+            var file = context.State.Environment["io"].Read<LuaTable>()["stdio"].Read<FileHandle>();
+            buffer.Span[0] = new LuaFunction("iterator", (context, buffer, ct) =>
+            {
+                var resultCount = IOHelper.Read(context.State, file, "lines", 0, [], buffer, true);
+                if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil)
+                {
+                    file.Close();
+                }
+                return new(resultCount);
+            });
+            return new(1);
+        }
+        else
+        {
+            var fileName = context.GetArgument<string>(0);
+
+            using var methodBuffer = new PooledArray<LuaValue>(32);
+            IOHelper.Open(context.State, fileName, "r", methodBuffer.AsMemory(), true);
+
+            var file = methodBuffer[0].Read<FileHandle>();
+            var formats = context.Arguments[1..].ToArray();
+
+            buffer.Span[0] = new LuaFunction("iterator", (context, buffer, ct) =>
+            {
+                var resultCount = IOHelper.Read(context.State, file, "lines", 0, formats, buffer, true);
+                if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil)
+                {
+                    file.Close();
+                }
+                return new(resultCount);
+            });
+
+            return new(1);
+        }
+    }
+
+    public static ValueTask<int> Open(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var fileName = context.GetArgument<string>(0);
+        var mode = context.HasArgument(1)
+            ? context.GetArgument<string>(1)
+            : "r";
+
+        var resultCount = IOHelper.Open(context.State, fileName, mode, buffer, false);
+        return new(resultCount);
+    }
+
+    public static ValueTask<int> Output(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var io = context.State.Environment["io"].Read<LuaTable>();
+
+        if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
+        {
+            buffer.Span[0] = io["stdout"];
+            return new(1);
+        }
+
+        var arg = context.Arguments[0];
+        if (arg.TryRead<FileHandle>(out var file))
+        {
+            io["stdout"] = file;
+            buffer.Span[0] = file;
+            return new(1);
+        }
+        else
+        {
+            var stream = File.Open(arg.ToString()!, FileMode.Open, FileAccess.ReadWrite);
+            var handle = new FileHandle(stream);
+            io["stdout"] = handle;
+            buffer.Span[0] = handle;
+            return new(1);
+        }
+    }
+
+    public static ValueTask<int> Read(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var file = context.State.Environment["io"].Read<LuaTable>()["stdio"].Read<FileHandle>();
+        var resultCount = IOHelper.Read(context.State, file, "read", 0, context.Arguments, buffer, false);
+        return new(resultCount);
+    }
+
+    public static ValueTask<int> Type(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument(0);
+
+        if (arg0.TryRead<FileHandle>(out var file))
+        {
+            buffer.Span[0] = file.IsClosed ? "closed file" : "file";
+        }
+        else
+        {
+            buffer.Span[0] = LuaValue.Nil;
+        }
+
+        return new(1);
+    }
+
+    public static ValueTask<int> Write(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var file = context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
+        var resultCount = IOHelper.Write(file, "write", context, buffer);
+        return new(resultCount);
+    }
+}

+ 4 - 4
src/Lua/Standard/Bitwise/Bit32Helper.cs → src/Lua/Standard/Internal/Bit32Helper.cs

@@ -1,6 +1,6 @@
 using System.Runtime.CompilerServices;
 
-namespace Lua.Standard.Bitwise;
+namespace Lua.Standard.Internal;
 
 internal static class Bit32Helper
 {
@@ -18,15 +18,15 @@ internal static class Bit32Helper
         return (int)(long)Math.IEEERemainder(d, Bit32);
     }
 
-    public static void ValidateFieldAndWidth(LuaState state, LuaFunction function, int argumentId, int field, int width)
+    public static void ValidateFieldAndWidth(LuaState state, string functionName, int argumentId, int field, int width)
     {
         if (field > 31 || (field + width) > 32)
             throw new LuaRuntimeException(state.GetTraceback(), "trying to access non-existent bits");
 
         if (field < 0)
-            throw new LuaRuntimeException(state.GetTraceback(), $"bad argument #{argumentId} to '{function.Name}' (field cannot be negative)");
+            throw new LuaRuntimeException(state.GetTraceback(), $"bad argument #{argumentId} to '{functionName}' (field cannot be negative)");
 
         if (width <= 0)
-            throw new LuaRuntimeException(state.GetTraceback(), "bad argument #{argumentId} to '{function.Name}' (width must be positive)");
+            throw new LuaRuntimeException(state.GetTraceback(), $"bad argument #{argumentId} to '{functionName}' (width must be positive)");
     }
 }

+ 48 - 48
src/Lua/Standard/OperatingSystem/DateFunction.cs → src/Lua/Standard/Internal/DateTimeHelper.cs

@@ -1,66 +1,66 @@
 using System.Runtime.CompilerServices;
 using System.Text;
 
-namespace Lua.Standard.OperatingSystem;
+namespace Lua.Standard.Internal;
 
-public sealed class DateFunction : LuaFunction
+internal static class DateTimeHelper
 {
-    public override string Name => "date";
-    public static readonly DateFunction Instance = new();
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static double GetUnixTime(DateTime dateTime)
+    {
+        return GetUnixTime(dateTime, DateTime.UnixEpoch);
+    }
 
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static double GetUnixTime(DateTime dateTime, DateTime epoch)
     {
-        var format = context.HasArgument(0)
-            ? context.GetArgument<string>(0).AsSpan()
-            : "%c".AsSpan();
+        var time = (dateTime - epoch).TotalSeconds;
+        if (time < 0.0) return 0;
+        return time;
+    }
 
-        DateTime now;
-        if (context.HasArgument(1))
-        {
-            var time = context.GetArgument<double>(1);
-            now = DateTimeHelper.FromUnixTime(time);
-        }
-        else
-        {
-            now = DateTime.UtcNow;
-        }
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static DateTime FromUnixTime(double unixTime)
+    {
+        var ts = TimeSpan.FromSeconds(unixTime);
+        return DateTime.UnixEpoch + ts;
+    }
 
-        var isDst = false;
-        if (format[0] == '!')
-        {
-            format = format[1..];
-        }
-        else
+    public static DateTime ParseTimeTable(LuaState state, LuaTable table)
+    {
+        static int GetTimeField(LuaState state, LuaTable table, string key, bool required = true, int defaultValue = 0)
         {
-            now = TimeZoneInfo.ConvertTimeFromUtc(now, TimeZoneInfo.Local);
-            isDst = now.IsDaylightSavingTime();
-        }
+            if (!table.TryGetValue(key, out var value))
+            {
+                if (required)
+                {
+                    throw new LuaRuntimeException(state.GetTraceback(), $"field '{key}' missing in date table");
+                }
+                else
+                {
+                    return defaultValue;
+                }
+            }
 
-        if (format == "*t")
-        {
-            var table = new LuaTable();
-            
-            table["year"] = now.Year;
-            table["month"] = now.Month;
-            table["day"] = now.Day;
-            table["hour"] = now.Hour;
-            table["min"] = now.Minute;
-            table["sec"] = now.Second;
-            table["wday"] = ((int)now.DayOfWeek) + 1;
-            table["yday"] = now.DayOfYear;
-            table["isdst"] = isDst;
-
-            buffer.Span[0] = table;
-        }
-        else
-        {
-            buffer.Span[0] = StrFTime(context.State, format, now);
+            if (value.TryRead<double>(out var d) && MathEx.IsInteger(d))
+            {
+                return (int)d;
+            }
+
+            throw new LuaRuntimeException(state.GetTraceback(), $"field '{key}' is not an integer");
         }
 
-        return new(1);
+        var day = GetTimeField(state, table, "day");
+        var month = GetTimeField(state, table, "month");
+        var year = GetTimeField(state, table, "year");
+        var sec = GetTimeField(state, table, "sec", false, 0);
+        var min = GetTimeField(state, table, "min", false, 0);
+        var hour = GetTimeField(state, table, "hour", false, 12);
+
+        return new DateTime(year, month, day, hour, min, sec);
     }
 
-    static string StrFTime(LuaState state, ReadOnlySpan<char> format, DateTime d)
+    public static string StrFTime(LuaState state, ReadOnlySpan<char> format, DateTime d)
     {
         // reference: http://www.cplusplus.com/reference/ctime/strftime/
 

+ 1 - 2
src/Lua/Standard/IO/IOHelper.cs → src/Lua/Standard/Internal/IOHelper.cs

@@ -1,8 +1,7 @@
-using System.Buffers.Text;
 using System.Text;
 using Lua.Internal;
 
-namespace Lua.Standard.IO;
+namespace Lua.Standard.Internal;
 
 internal static class IOHelper
 {

+ 0 - 16
src/Lua/Standard/Mathematics/AbsFunction.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class AbsFunction : LuaFunction
-{
-    public static readonly AbsFunction Instance = new();
-
-    public override string Name => "abs";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Abs(arg0);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/AcosFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class AcosFunction : LuaFunction
-{
-    public static readonly AcosFunction Instance = new();
-
-    public override string Name => "acos";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Acos(arg0);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/AsinFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class AsinFunction : LuaFunction
-{
-    public static readonly AsinFunction Instance = new();
-
-    public override string Name => "asin";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Asin(arg0);
-        return new(1);
-    }
-}

+ 0 - 18
src/Lua/Standard/Mathematics/Atan2Function.cs

@@ -1,18 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class Atan2Function : LuaFunction
-{
-    public static readonly Atan2Function Instance = new();
-
-    public override string Name => "atan2";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        var arg1 = context.GetArgument<double>(1);
-
-        buffer.Span[0] = Math.Atan2(arg0, arg1);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/AtanFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class AtanFunction : LuaFunction
-{
-    public static readonly AtanFunction Instance = new();
-
-    public override string Name => "atan";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Atan(arg0);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/CeilFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class CeilFunction : LuaFunction
-{
-    public static readonly CeilFunction Instance = new();
-
-    public override string Name => "ceil";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Ceiling(arg0);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/CosFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class CosFunction : LuaFunction
-{
-    public static readonly CosFunction Instance = new();
-
-    public override string Name => "cos";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Cos(arg0);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/CoshFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class CoshFunction : LuaFunction
-{
-    public static readonly CoshFunction Instance = new();
-
-    public override string Name => "cosh";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Cosh(arg0);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/DegFunction.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class DegFunction : LuaFunction
-{
-    public static readonly DegFunction Instance = new();
-
-    public override string Name => "deg";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = arg0 * (180.0 / Math.PI);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/ExpFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class ExpFunction : LuaFunction
-{
-    public static readonly ExpFunction Instance = new();
-
-    public override string Name => "exp";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Exp(arg0);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/FloorFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class FloorFunction : LuaFunction
-{
-    public static readonly FloorFunction Instance = new();
-
-    public override string Name => "floor";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Floor(arg0);
-        return new(1);
-    }
-}

+ 0 - 17
src/Lua/Standard/Mathematics/FmodFunction.cs

@@ -1,17 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class FmodFunction : LuaFunction
-{
-    public static readonly FmodFunction Instance = new();
-
-    public override string Name => "fmod";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        var arg1 = context.GetArgument<double>(1);
-        buffer.Span[0] = arg0 % arg1;
-        return new(1);
-    }
-}

+ 0 - 19
src/Lua/Standard/Mathematics/FrexpFunction.cs

@@ -1,19 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class FrexpFunction : LuaFunction
-{
-    public static readonly FrexpFunction Instance = new();
-
-    public override string Name => "frexp";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-
-        var (m, e) = MathEx.Frexp(arg0);
-        buffer.Span[0] = m;
-        buffer.Span[1] = e;
-        return new(2);
-    }
-}

+ 0 - 18
src/Lua/Standard/Mathematics/LdexpFunction.cs

@@ -1,18 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class LdexpFunction : LuaFunction
-{
-    public static readonly LdexpFunction Instance = new();
-
-    public override string Name => "ldexp";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        var arg1 = context.GetArgument<double>(1);
-
-        buffer.Span[0] = arg0 * Math.Pow(2, arg1);
-        return new(1);
-    }
-}

+ 0 - 26
src/Lua/Standard/Mathematics/LogFunction.cs

@@ -1,26 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class LogFunction : LuaFunction
-{
-    public static readonly LogFunction Instance = new();
-
-    public override string Name => "log";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-
-        if (context.ArgumentCount == 1)
-        {
-            buffer.Span[0] = Math.Log(arg0);
-        }
-        else
-        {
-            var arg1 = context.GetArgument<double>(1);
-            buffer.Span[0] = Math.Log(arg0, arg1);
-        }
-
-        return new(1);
-    }
-}

+ 0 - 22
src/Lua/Standard/Mathematics/MaxFunction.cs

@@ -1,22 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class MaxFunction : LuaFunction
-{
-    public static readonly MaxFunction Instance = new();
-
-    public override string Name => "max";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var x = context.GetArgument<double>(0);
-        for (int i = 1; i < context.ArgumentCount; i++)
-        {
-            x = Math.Max(x, context.GetArgument<double>(i));
-        }
-
-        buffer.Span[0] = x;
-
-        return new(1);
-    }
-}

+ 0 - 22
src/Lua/Standard/Mathematics/MinFunction.cs

@@ -1,22 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class MinFunction : LuaFunction
-{
-    public static readonly MinFunction Instance = new();
-
-    public override string Name => "min";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var x = context.GetArgument<double>(0);
-        for (int i = 1; i < context.ArgumentCount; i++)
-        {
-            x = Math.Min(x, context.GetArgument<double>(i));
-        }
-
-        buffer.Span[0] = x;
-
-        return new(1);
-    }
-}

+ 0 - 18
src/Lua/Standard/Mathematics/ModfFunction.cs

@@ -1,18 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class ModfFunction : LuaFunction
-{
-    public static readonly ModfFunction Instance = new();
-
-    public override string Name => "modf";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        var (i, f) = MathEx.Modf(arg0);
-        buffer.Span[0] = i;
-        buffer.Span[1] = f;
-        return new(2);
-    }
-}

+ 0 - 18
src/Lua/Standard/Mathematics/PowFunction.cs

@@ -1,18 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class PowFunction : LuaFunction
-{
-    public static readonly PowFunction Instance = new();
-
-    public override string Name => "pow";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        var arg1 = context.GetArgument<double>(1);
-
-        buffer.Span[0] = Math.Pow(arg0, arg1);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/RadFunction.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class RadFunction : LuaFunction
-{
-    public static readonly RadFunction Instance = new();
-
-    public override string Name => "rad";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = arg0 * (Math.PI / 180.0);
-        return new(1);
-    }
-}

+ 0 - 33
src/Lua/Standard/Mathematics/RandomFunction.cs

@@ -1,33 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class RandomFunction : LuaFunction
-{
-    public const string RandomInstanceKey = "__lua_mathematics_library_random_instance";
-    public static readonly RandomFunction Instance = new();
-
-    public override string Name => "random";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var rand = context.State.Environment[RandomInstanceKey].Read<LuaUserData<Random>>().Value;
-
-        if (context.ArgumentCount == 0)
-        {
-            buffer.Span[0] = rand.NextDouble();
-        }
-        else if (context.ArgumentCount == 1)
-        {
-            var arg0 = context.GetArgument<double>(0);
-            buffer.Span[0] = rand.NextDouble() * (arg0 - 1) + 1;
-        }
-        else
-        {
-            var arg0 = context.GetArgument<double>(0);
-            var arg1 = context.GetArgument<double>(1);
-            buffer.Span[0] = rand.NextDouble() * (arg1 - arg0) + arg0;
-        }
-
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/RandomSeedFunction.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class RandomSeedFunction : LuaFunction
-{
-    public static readonly RandomSeedFunction Instance = new();
-
-    public override string Name => "randomseed";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        context.State.Environment[RandomFunction.RandomInstanceKey] = new LuaUserData<Random>(new Random((int)BitConverter.DoubleToInt64Bits(arg0)));
-        return new(0);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/SinFuncion.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class SinFunction : LuaFunction
-{
-    public static readonly SinFunction Instance = new();
-
-    public override string Name => "sin";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Sin(arg0);
-        return new(1);
-    }
-}

+ 0 - 15
src/Lua/Standard/Mathematics/SinhFuncion.cs

@@ -1,15 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-public sealed class SinhFunction : LuaFunction
-{
-    public static readonly SinhFunction Instance = new();
-
-    public override string Name => "sinh";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Sinh(arg0);
-        return new(1);
-    }
-}

+ 0 - 16
src/Lua/Standard/Mathematics/SqrtFunction.cs

@@ -1,16 +0,0 @@
-
-namespace Lua.Standard.Mathematics;
-
-public sealed class SqrtFunction : LuaFunction
-{
-    public static readonly SqrtFunction Instance = new();
-
-    public override string Name => "sqrt";
-
-    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-    {
-        var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Sqrt(arg0);
-        return new(1);
-    }
-}

Some files were not shown because too many files changed in this diff