Browse Source

Add: next, pairs

AnnulusGames 1 year ago
parent
commit
49c7cd8a67

+ 45 - 1
src/Lua/LuaTable.cs

@@ -1,6 +1,5 @@
 using System.Runtime.CompilerServices;
 using Lua.Internal;
-using Lua.Runtime;
 
 namespace Lua;
 
@@ -134,6 +133,51 @@ public sealed class LuaTable
         return dictionary.ContainsKey(key);
     }
 
+    public KeyValuePair<LuaValue, LuaValue> GetNext(LuaValue key)
+    {
+        var index = -1;
+        if (key.Type is LuaValueType.Nil)
+        {
+            index = 0;
+        }
+        else if (TryGetInteger(key, out var integer) && integer > 0 && integer <= array.Length)
+        {
+            index = integer;
+        }
+
+        if (index != -1)
+        {
+            var span = array.AsSpan(index);
+            for (int i = 0; i < span.Length; i++)
+            {
+                if (span[i].Type is not LuaValueType.Nil)
+                {
+                    return new(index + i + 1, span[i]);
+                }
+            }
+
+            foreach (var pair in dictionary)
+            {
+                return pair;
+            }
+        }
+        else
+        {
+            var foundKey = false;
+            foreach (var pair in dictionary)
+            {
+                if (foundKey) return pair;
+
+                if (pair.Key.Equals(key))
+                {
+                    foundKey = true;
+                }
+            }
+        }
+
+        return default;
+    }
+
     public void Clear()
     {
         dictionary.Clear();

+ 1 - 0
src/Lua/Runtime/Metamethods.cs

@@ -18,6 +18,7 @@ public static class Metamethods
     public const string Le = "__le";
     public const string Call = "__call";
     public const string Concat = "__concat";
+    public const string Pairs = "__pairs";
     public const string IPairs = "__ipairs";
     public new const string ToString = "__tostring";
 }

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

@@ -0,0 +1,18 @@
+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.ReadArgument<LuaTable>(0);
+        var arg1 = context.ArgumentCount >= 2 ? context.Arguments[1] : LuaValue.Nil;
+
+        var kv = arg0.GetNext(arg1);
+        buffer.Span[0] = kv.Key;
+        buffer.Span[1] = kv.Value;
+        return new(2);
+    }
+}

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

@@ -0,0 +1,42 @@
+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.ReadArgument<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.GetTracebacks(), "call", metamethod);
+            }
+
+            return function.InvokeAsync(context, buffer, cancellationToken);
+        }
+
+        buffer.Span[0] = new Iterator(arg0);
+        return new(1);
+    }
+
+    class Iterator(LuaTable table) : LuaFunction
+    {
+        LuaValue key;
+
+        protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+        {
+            var kv = table.GetNext(key);
+            buffer.Span[0] = kv.Key;
+            buffer.Span[1] = kv.Value;
+            key = kv.Key;
+            return new(2);
+        }
+    }
+}