Browse Source

Merge pull request #10 from AnnulusGames/module-library

Add module library
Annulus Games 1 year ago
parent
commit
8ef286bdc0

+ 3 - 1
src/Lua/Exceptions.cs

@@ -87,4 +87,6 @@ public class LuaAssertionException(Traceback traceback, string message) : LuaRun
     {
     {
         return $"{Message}\n{StackTrace}";
         return $"{Message}\n{StackTrace}";
     }
     }
-}
+}
+
+public class LuaModuleNotFoundException(string moduleName) : LuaException($"module '{moduleName}' not found");

+ 7 - 0
src/Lua/ILuaModuleLoader.cs

@@ -0,0 +1,7 @@
+namespace Lua;
+
+public interface ILuaModuleLoader
+{
+    bool Exists(string moduleName);
+    ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default);
+}

+ 242 - 0
src/Lua/Loaders/CompositeModuleLoader.cs

@@ -0,0 +1,242 @@
+
+namespace Lua.Loaders;
+
+public static class CompositeModuleLoader
+{
+    class CompositeLoader_2(ILuaModuleLoader loader0, ILuaModuleLoader loader1) : ILuaModuleLoader
+    {
+        public bool Exists(string moduleName)
+        {
+            return loader0.Exists(moduleName) &&
+                loader1.Exists(moduleName);
+        }
+
+        public ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default)
+        {
+            if (loader0.Exists(moduleName))
+            {
+                return loader0.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader1.Exists(moduleName))
+            {
+                return loader1.LoadAsync(moduleName, cancellationToken);
+            }
+
+            throw new LuaModuleNotFoundException(moduleName);
+        }
+    }
+
+    class CompositeLoader_3(ILuaModuleLoader loader0, ILuaModuleLoader loader1, ILuaModuleLoader loader2) : ILuaModuleLoader
+    {
+        public bool Exists(string moduleName)
+        {
+            return loader0.Exists(moduleName) &&
+                loader1.Exists(moduleName) &&
+                loader2.Exists(moduleName);
+        }
+
+        public ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default)
+        {
+            if (loader0.Exists(moduleName))
+            {
+                return loader0.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader1.Exists(moduleName))
+            {
+                return loader1.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader2.Exists(moduleName))
+            {
+                return loader2.LoadAsync(moduleName, cancellationToken);
+            }
+
+            throw new LuaModuleNotFoundException(moduleName);
+        }
+    }
+
+    class CompositeLoader_4(ILuaModuleLoader loader0, ILuaModuleLoader loader1, ILuaModuleLoader loader2, ILuaModuleLoader loader3) : ILuaModuleLoader
+    {
+        public bool Exists(string moduleName)
+        {
+            return loader0.Exists(moduleName) &&
+                loader1.Exists(moduleName) &&
+                loader2.Exists(moduleName) &&
+                loader3.Exists(moduleName);
+        }
+
+        public ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default)
+        {
+            if (loader0.Exists(moduleName))
+            {
+                return loader0.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader1.Exists(moduleName))
+            {
+                return loader1.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader2.Exists(moduleName))
+            {
+                return loader2.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader3.Exists(moduleName))
+            {
+                return loader3.LoadAsync(moduleName, cancellationToken);
+            }
+
+            throw new LuaModuleNotFoundException(moduleName);
+        }
+    }
+
+    class CompositeLoader_5(ILuaModuleLoader loader0, ILuaModuleLoader loader1, ILuaModuleLoader loader2, ILuaModuleLoader loader3, ILuaModuleLoader loader4) : ILuaModuleLoader
+    {
+        public bool Exists(string moduleName)
+        {
+            return loader0.Exists(moduleName) &&
+                loader1.Exists(moduleName) &&
+                loader2.Exists(moduleName) &&
+                loader3.Exists(moduleName) &&
+                loader4.Exists(moduleName);
+        }
+
+        public ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default)
+        {
+            if (loader0.Exists(moduleName))
+            {
+                return loader0.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader1.Exists(moduleName))
+            {
+                return loader1.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader2.Exists(moduleName))
+            {
+                return loader2.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader3.Exists(moduleName))
+            {
+                return loader3.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader4.Exists(moduleName))
+            {
+                return loader4.LoadAsync(moduleName, cancellationToken);
+            }
+
+            throw new LuaModuleNotFoundException(moduleName);
+        }
+    }
+
+    class CompositeLoader_6(ILuaModuleLoader loader0, ILuaModuleLoader loader1, ILuaModuleLoader loader2, ILuaModuleLoader loader3, ILuaModuleLoader loader4, ILuaModuleLoader loader5) : ILuaModuleLoader
+    {
+        public bool Exists(string moduleName)
+        {
+            return loader0.Exists(moduleName) &&
+                loader1.Exists(moduleName) &&
+                loader2.Exists(moduleName) &&
+                loader3.Exists(moduleName) &&
+                loader4.Exists(moduleName) &&
+                loader5.Exists(moduleName);
+        }
+
+        public ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default)
+        {
+            if (loader0.Exists(moduleName))
+            {
+                return loader0.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader1.Exists(moduleName))
+            {
+                return loader1.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader2.Exists(moduleName))
+            {
+                return loader2.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader3.Exists(moduleName))
+            {
+                return loader3.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader4.Exists(moduleName))
+            {
+                return loader4.LoadAsync(moduleName, cancellationToken);
+            }
+
+            if (loader5.Exists(moduleName))
+            {
+                return loader5.LoadAsync(moduleName, cancellationToken);
+            }
+
+            throw new LuaModuleNotFoundException(moduleName);
+        }
+    }
+
+
+    class CompositeLoader(ILuaModuleLoader[] loaders) : ILuaModuleLoader
+    {
+        public bool Exists(string moduleName)
+        {
+            foreach (var loader in loaders)
+            {
+                if (loader.Exists(moduleName)) return true;
+            }
+
+            return false;
+        }
+
+        public ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default)
+        {
+            foreach (var loader in loaders)
+            {
+                if (loader.Exists(moduleName))
+                {
+                    return loader.LoadAsync(moduleName, cancellationToken);
+                }
+            }
+
+            throw new LuaModuleNotFoundException(moduleName);
+        }
+    }
+
+    public static ILuaModuleLoader Create(ILuaModuleLoader loader0, ILuaModuleLoader loader1)
+    {
+        return new CompositeLoader_2(loader0, loader1);
+    }
+
+    public static ILuaModuleLoader Create(ILuaModuleLoader loader0, ILuaModuleLoader loader1, ILuaModuleLoader loader2)
+    {
+        return new CompositeLoader_3(loader0, loader1, loader2);
+    }
+
+    public static ILuaModuleLoader Create(ILuaModuleLoader loader0, ILuaModuleLoader loader1, ILuaModuleLoader loader2, ILuaModuleLoader loader3)
+    {
+        return new CompositeLoader_4(loader0, loader1, loader2, loader3);
+    }
+
+    public static ILuaModuleLoader Create(ILuaModuleLoader loader0, ILuaModuleLoader loader1, ILuaModuleLoader loader2, ILuaModuleLoader loader3, ILuaModuleLoader loader4)
+    {
+        return new CompositeLoader_5(loader0, loader1, loader2, loader3, loader4);
+    }
+
+    public static ILuaModuleLoader Create(ILuaModuleLoader loader0, ILuaModuleLoader loader1, ILuaModuleLoader loader2, ILuaModuleLoader loader3, ILuaModuleLoader loader4, ILuaModuleLoader loader5)
+    {
+        return new CompositeLoader_6(loader0, loader1, loader2, loader3, loader4, loader5);
+    }
+
+    public static ILuaModuleLoader Create(params ILuaModuleLoader[] loaders)
+    {
+        return new CompositeLoader(loaders);
+    }
+}

+ 20 - 0
src/Lua/Loaders/FileModuleLoader.cs

@@ -0,0 +1,20 @@
+
+namespace Lua.Loaders;
+
+public sealed class FileModuleLoader : ILuaModuleLoader
+{
+    public static readonly FileModuleLoader Instance = new();
+
+    public bool Exists(string moduleName)
+    {
+        return File.Exists(moduleName);
+    }
+
+    public async ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default)
+    {
+        var path = moduleName;
+        if (!Path.HasExtension(path)) path += ".lua";
+        var text = await File.ReadAllTextAsync(path, cancellationToken);
+        return new LuaModule(moduleName, text);
+    }
+}

+ 29 - 0
src/Lua/LuaModule.cs

@@ -0,0 +1,29 @@
+namespace Lua;
+
+public enum LuaModuleType
+{
+    Text,
+}
+
+public readonly struct LuaModule
+{
+    public string Name => name;
+    public LuaModuleType Type => type;
+
+    readonly string name;
+    readonly LuaModuleType type;
+    readonly object referenceValue;
+
+    public LuaModule(string name, string text)
+    {
+        this.name = name;
+        type = LuaModuleType.Text;
+        referenceValue = text;
+    }
+
+    public string ReadText()
+    {
+        if (type != LuaModuleType.Text) throw new Exception(); // TODO: add message
+        return (string)referenceValue;
+    }
+}

+ 3 - 0
src/Lua/LuaState.cs

@@ -1,4 +1,5 @@
 using Lua.Internal;
 using Lua.Internal;
+using Lua.Loaders;
 using Lua.Runtime;
 using Lua.Runtime;
 
 
 namespace Lua;
 namespace Lua;
@@ -29,6 +30,8 @@ public sealed class LuaState
         }
         }
     }
     }
 
 
+    public ILuaModuleLoader ModuleLoader { get; set; } = FileModuleLoader.Instance;
+
     public static LuaState Create()
     public static LuaState Create()
     {
     {
         return new();
         return new();

+ 33 - 0
src/Lua/Standard/Modules/RequireFunction.cs

@@ -0,0 +1,33 @@
+
+using Lua.CodeAnalysis.Compilation;
+using Lua.Internal;
+using Lua.Runtime;
+
+namespace Lua.Standard.Modules;
+
+public sealed class RequireFunction : LuaFunction
+{
+    public override string Name => "require";
+    public static readonly RequireFunction Instance = new();
+
+    protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.ReadArgument<string>(0);
+        var loaded = context.State.Environment["package"].Read<LuaTable>()["loaded"].Read<LuaTable>();
+
+        if (!loaded.TryGetValue(arg0, out var loadedTable))
+        {
+            var module = await context.State.ModuleLoader.LoadAsync(arg0, cancellationToken);
+            var chunk = LuaCompiler.Default.Compile(module.ReadText(), module.Name);
+
+            using var methodBuffer = new PooledArray<LuaValue>(1);
+            await new Closure(context.State, chunk).InvokeAsync(context, methodBuffer.AsMemory(), cancellationToken);
+
+            loadedTable = methodBuffer[0];
+            loaded[arg0] = loadedTable;
+        }
+
+        buffer.Span[0] = loadedTable;
+        return 1;
+    }
+}

+ 10 - 0
src/Lua/Standard/OpenLibExtensions.cs

@@ -1,6 +1,7 @@
 using Lua.Standard.Basic;
 using Lua.Standard.Basic;
 using Lua.Standard.Coroutines;
 using Lua.Standard.Coroutines;
 using Lua.Standard.Mathematics;
 using Lua.Standard.Mathematics;
+using Lua.Standard.Modules;
 
 
 namespace Lua.Standard;
 namespace Lua.Standard;
 
 
@@ -97,4 +98,13 @@ public static class OpenLibExtensions
 
 
         state.Environment["math"] = table;
         state.Environment["math"] = table;
     }
     }
+
+    public static void OpenModuleLibrary(this LuaState state)
+    {
+        var package = new LuaTable(0, 1);
+        package["loaded"] = new LuaTable();
+        state.Environment["package"] = package;
+        
+        state.Environment[RequireFunction.Instance.Name] = RequireFunction.Instance;
+    }
 }
 }