Browse Source

Add: README

AnnulusGames 1 year ago
parent
commit
2b368243fd
2 changed files with 670 additions and 0 deletions
  1. 330 0
      README.md
  2. 340 0
      README_JA.md

+ 330 - 0
README.md

@@ -0,0 +1,330 @@
+# Lua-CSharp
+
+High performance Lua interpreter implemented in C# for .NET and Unity
+
+[![NuGet](https://img.shields.io/nuget/v/LuaCSharp.svg)](https://www.nuget.org/packages/Lua)
+[![Releases](https://img.shields.io/github/release/AnnulusGames/Lua-CSharp.svg)](https://github.com/AnnulusGames/Lua-CSharp/releases)
+[![license](https://img.shields.io/badge/LICENSE-MIT-green.svg)](LICENSE)
+
+English | [日本語](README_JA.md)
+
+## Overview
+
+Lua-CSharp is a library that provides a Lua interpreter implemented in C#. By integrating Lua-CSharp, you can easily embed Lua scripts into your .NET applications.
+
+// TODO: Add benchmarks
+
+## Features
+
+* Lua 5.2 interpreter implemented in C#
+* Easy-to-use API integrated with async/await
+* Support for exception handling with try-catch
+* High-performance implementation utilizing modern C#
+* Unity support (works with both Mono and IL2CPP)
+
+## Installation
+
+### NuGet packages
+
+To use Lua-CSharp, .NET Standard 2.1 or higher is required. The package can be obtained from NuGet.
+
+### .NET CLI
+
+```ps1
+dotnet add package LuaCSharp
+```
+
+### Package Manager
+
+```ps1
+Install-Package LuaCSharp
+```
+
+### Unity
+
+Lua-CSharp can also be used in Unity (works with both Mono and IL2CPP).
+
+### Requirements
+
+* Unity 2021.3 or higher
+
+### Installation
+
+1. Install [NugetForUnity](https://github.com/GlitchEnzo/NuGetForUnity).
+2. Open the NuGet window by going to `NuGet > Manage NuGet Packages`, search for the `LuaCSharp` package, and install it.
+
+## Quick Start
+
+By using the `LuaState` class, you can execute Lua scripts from C#. Below is a sample code that evaluates a simple calculation written in Lua.
+
+```cs
+using Lua;
+
+// Create a LuaState
+var state = LuaState.Create();
+
+// Execute a Lua script string with DoStringAsync
+var results = await state.DoStringAsync("return 1 + 1");
+
+// 2
+Console.WriteLine(results[0]);
+```
+
+> [!WARNING]
+> `LuaState` is not thread-safe. Do not access it from multiple threads simultaneously.
+
+## LuaValue
+
+Values in Lua scripts are represented by the `LuaValue` type. The value of a `LuaValue` can be read using `TryRead<T>(out T value)` or `Read<T>()`.
+
+```cs
+var results = await state.DoStringAsync("return 1 + 1");
+
+// double
+var value = results[0].Read<double>();
+```
+
+You can also get the type of the value from the `Type` property.
+
+```cs
+var isNil = results[0].Type == LuaValueType.Nil;
+```
+
+Below is a table showing the type mapping between Lua and C#.
+
+| Lua        | C#                |
+| ---------- | ----------------- |
+| `nil`      | `LuaValue.Nil`    |
+| `boolean`  | `bool`            |
+| `string`   | `string`          |
+| `number`   | `double`, `float` |
+| `table`    | `LuaTable`        |
+| `function` | `LuaFunction`     |
+| `userdata` | `LuaUserData`     |
+| `thread`   | `LuaThread`       |
+
+When creating a `LuaValue` from the C# side, compatible types are implicitly converted into `LuaValue`.
+
+```cs
+LuaValue value;
+value = 1.2;           // double   ->  LuaValue
+value = "foo";         // string   ->  LuaValue
+value = new LuaTable() // LuaTable ->  LuaValue
+```
+
+## LuaTable
+
+Lua tables are represented by the `LuaTable` type. They can be used similarly to `LuaValue[]` or `Dictionary<LuaValue, LuaValue>`.
+
+```cs
+// Create a table in Lua
+var results = await state.DoStringAsync("return { a = 1, b = 2, c = 3 }");
+var table1 = results[0].Read<LuaTable>();
+
+// 1
+Console.WriteLine(table1["a"]);
+
+// Create a table in C#
+results = await state.DoStringAsync("return { 1, 2, 3 }");
+var table2 = results[0].Read<LuaTable>();
+
+// 1 (Note: Lua arrays are 1-indexed)
+Console.WriteLine(table2[1]);
+```
+
+## Global Environment
+
+You can access Lua's global environment through `state.Environment`. This table allows for easy value exchange between Lua and C#.
+
+```cs
+// Set a = 10
+state.Environment["a"] = 10;
+
+var results = await state.DoStringAsync("return a");
+
+// 10
+Console.WriteLine(results[0]);
+```
+
+## Standard Libraries
+
+You can use Lua's standard libraries as well. By calling `state.OpenStandardLibraries()`, the standard library tables are added to the `LuaState`.
+
+```cs
+using Lua;
+using Lua.Standard;
+
+var state = LuaState.Create();
+
+// Add standard libraries
+state.OpenStandardLibraries();
+
+var results = await state.DoStringAsync("return math.pi");
+Console.WriteLine(results[0]); // 3.141592653589793
+```
+
+For more details on standard libraries, refer to the [Lua official manual](https://www.lua.org/manual/5.2/manual.html#6).
+
+> [!WARNING]
+> Lua-CSharp does not support all functions of the standard libraries. For details, refer to the [Compatibility](#compatibility) section.
+
+## Functions
+
+Lua functions are represented by the `LuaFunction` type. With `LuaFunction`, you can call Lua functions from C#, or define functions in C# that can be called from Lua.
+
+### Calling Lua Functions from C#
+
+```lua
+-- lua2cs.lua
+
+local function add(a, b)
+    return a + b
+end
+
+return add;
+```
+
+```cs
+var results = await state.DoFileAsync("lua2cs.lua");
+var func = results[0].Read<LuaFunction>();
+
+// Execute the function with arguments
+var funcResults = await func.InvokeAsync(state, new[] { 1, 2 });
+
+// 3
+Console.WriteLine(funcResults[0]);
+```
+
+### Calling C# Functions from Lua
+
+You can create a `LuaFunction` from a lambda expression using `LuaFunction.Create()`.
+
+```cs
+// Add a function to the global environment
+state.Environment["add"] = LuaFunction.Create((args, ct) =>
+{
+    return new[] { args[0].Read<double>() + args[1].Read<double>() };
+});
+
+// Execute Lua script
+var results = await state.DoFileAsync("cs2lua.lua");
+
+// 3
+Console.WriteLine(results[0]);
+```
+
+```lua
+-- cs2lua.lua
+
+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`.
+
+## Coroutines
+
+Lua coroutines are represented by the `LuaThread` type.
+
+Coroutines can not only be used within Lua scripts, but you can also await Lua-created coroutines from C#.
+
+```lua
+-- coroutine.lua
+
+local co = coroutine.create(function()
+    for i = 1, 10 do
+        print(i)
+        coroutine.yield()
+    end
+end)
+
+return co
+```
+
+```cs
+var results = await state.DoFileAsync("coroutine.lua");
+var co = results[0].Read<LuaThread>();
+
+for (int i = 0; i < 10; i++)
+{
+    var resumeResults = await co.ResumeAsync(state);
+
+    // Similar to coroutine.resume(), returns true on success and the return values afterward
+    // 1, 2, 3, 4, ...
+    Console.WriteLine(resumeResults[1]);
+}
+```
+
+## Module Loading
+
+In Lua, you can load modules using the `require` function. In regular Lua, modules are managed by searchers within the `package.searchers` function list. In Lua-CSharp, this is replaced by the `ILuaModuleLoader` interface.
+
+```cs
+public interface ILuaModuleLoader
+{
+    bool Exists(string moduleName);
+    ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default);
+}
+```
+
+You can set the `LuaState.ModuleLoader` to change how modules are loaded. By default, the `FileModuleLoader` is set to load modules from Lua files.
+
+You can also combine multiple loaders using `CompositeModuleLoader.Create(loader1, loader2, ...)`.
+
+```cs
+state.ModuleLoader = CompositeModuleLoader.Create(
+    new FileModuleLoader(),
+    new CustomModuleLoader()
+);
+```
+
+Loaded modules are cached in the `package.loaded` table, just like regular Lua. This can be accessed via `LuaState.LoadedModules`.
+
+## Exception Handling
+
+Lua script parsing errors and runtime exceptions throw exceptions that inherit from `LuaException`. You can catch these to handle errors during execution.
+
+```cs
+try
+{
+    await state.DoFileAsync("filename.lua");
+}
+catch (LuaParseException)
+{
+    // Handle parsing errors
+}
+catch (LuaRuntimeException)
+{
+    // Handle runtime exceptions
+}
+```
+
+## Compatibility
+
+Lua-CSharp is designed with integration into .NET in mind, so there are several differences from the C implementation.
+
+### Binary
+
+Lua-CSharp does not support Lua bytecode (tools like `luac` cannot be used). Only Lua source code can be executed.
+
+### Character Encoding
+
+The character encoding used in Lua-CSharp is UTF-16. Since standard Lua assumes a single-byte character encoding, string behavior differs significantly.
+
+For example, in regular Lua, the following code outputs `15`, but in Lua-CSharp, it outputs `5`.
+
+```lua
+local l = string.len("あいうえお")
+print(l)
+```
+
+All string library functions handle strings as UTF-16.
+
+### Garbage Collection
+
+Since Lua-CSharp is implemented in C#, it relies on .NET's garbage collector. As a result, memory management behavior differs from regular Lua.
+
+While `collectgarbage()` is available, it simply calls the corresponding .NET garbage collection method and may not exhibit the same behavior as C's Lua garbage collector.
+
+## License
+
+Lua-CSharp is licensed under the MIT License.

+ 340 - 0
README_JA.md

@@ -0,0 +1,340 @@
+# Lua-CSharp
+
+High performance Lua interpreter implemented in C# for .NET and Unity
+
+[![NuGet](https://img.shields.io/nuget/v/LuaCSharp.svg)](https://www.nuget.org/packages/Lua)
+[![Releases](https://img.shields.io/github/release/AnnulusGames/Lua-CSharp.svg)](https://github.com/AnnulusGames/Lua-CSharp/releases)
+[![license](https://img.shields.io/badge/LICENSE-MIT-green.svg)](LICENSE)
+
+[English]((./README.md)) | 日本語
+
+## 概要
+
+Lua-CSharpはC#実装のLuaインタプリタを提供するライブラリです。Lua-CSharpを導入することで、.NETアプリケーション内で簡単にLuaスクリプトを組み込むことが可能になります。
+
+// TODO: ベンチマークの追加
+
+## 特徴
+
+* C#で実装されたLua5.2インタプリタ
+* async/awaitに統合された扱いやすいAPI
+* try-catchによる例外処理のサポート
+* 最新のC#を活用したハイパフォーマンスな実装
+* Unityサポート(Mono/IL2CPPの両方で動作)
+
+## インストール
+
+### NuGet packages
+
+Lua-CSharpを利用するには.NET Standard2.1以上が必要です。パッケージはNuGetから入手できます。
+
+### .NET CLI
+
+```ps1
+dotnet add package LuaCSharp
+```
+
+### Package Manager
+
+```ps1
+Install-Package LuaCSharp
+```
+
+### Unity
+
+Lua-CSharpをUnityで利用することも可能です。(Mono/IL2CPPの両方で動作します)
+
+### 要件
+
+* Unity 2021.3 以上
+
+### インストール
+
+1. [NugetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)をインストールします。
+2. `NuGet > Manage NuGet Packages`からNuGetウィンドウを開き、`LuaCSharp`パッケージを検索してインストールします。
+
+## クイックスタート
+
+`LuaState`クラスを利用することでC#上からLuaスクリプトを実行することが可能です。以下はLuaで記述された簡単な演算を評価するサンプルコードです。
+
+```cs
+using Lua;
+
+// LuaStateを作成する
+var state = LuaState.Create();
+
+// DoStringAsyncで文字列のLuaスクリプトを実行する
+var results = await state.DoStringAsync("return 1 + 1");
+
+// 2
+Console.WriteLine(results[0]);
+```
+
+> [!WARNING]
+> `LuaState`はスレッドセーフではありません。同時に複数のスレッドからアクセスしないでください。
+
+## LuaValue
+
+Luaスクリプト上の値は`LuaValue`型で表現されます。`LuaValue`の値は`TryRead<T>(out T value)`または`Read<T>()`で読み取ることが可能です。
+
+```cs
+var results = await state.DoStringAsync("return 1 + 1");
+
+// double
+var value = results[0].Read<double>();
+```
+
+また、`Type`プロパティから値の型を取得できます。
+
+```cs
+var isNil = results[0].Type == LuaValueType.Nil;
+```
+
+Lua-C#間の型の対応を以下に示します。
+
+| Lua        | C#               |
+| ---------- | ---------------- |
+| `nil`      | `LuaValue.Nil`   |
+| `boolean`  | `bool`           |
+| `string`   | `string`         |
+| `number`   | `double`,`float` |
+| `table`    | `LuaTable`       |
+| `function` | `LuaFunction`    |
+| `userdata` | `LuaUserData`    |
+| `thread`   | `LuaThread`      |
+
+C#側から`LuaValue`を作成する際には、変換可能な型の場合であれば暗黙的に`LuaValue`に変換されます。
+
+```cs
+LuaValue value;
+value = 1.2;           // double   ->  LuaValue
+value = "foo";         // string   ->  LuaValue
+value = new LuaTable() // LuaTable ->  LuaValue
+```
+
+## LuaTable
+
+Luaのテーブルは`LuaTable`型で表現されます。これは通常の`LuaValue[]`や`Dictionary<LuaValue, LuaValue>`のように使用できます。
+
+```cs
+// Lua側でテーブルを作成
+var results = await state.DoStringAsync("return { a = 1, b = 2, c = 3 }")
+var table1 = results[0].Read<LuaTable>();
+
+// 1
+Console.WriteLine(table1["a"]);
+
+// テーブルを作成
+results = await state.DoStringAsync("return { 1, 2, 3 }")
+var table2 = results[0].Read<LuaTable>();
+
+// 1 (Luaの配列は1-originであることに注意)
+Console.WriteLine(table2[1]);
+```
+
+## グローバル環境
+
+`state.Environment`からLuaのグローバル環境にアクセスできます。このテーブルを介して簡単にLua-C#間で値をやり取りすることが可能です。
+
+```cs
+// a = 10を設定
+state.Environment["a"] = 10;
+
+var results = await state.DoStringAsync("return a");
+
+// 10
+Console.WriteLine(results[0]);
+```
+
+## 標準ライブラリ
+
+Luaの標準ライブラリを利用することも可能です。`state.OpenStandardLibraries()`を呼び出すことで、`LuaState`に標準ライブラリのテーブルを追加します。
+
+```cs
+using Lua;
+using Lua.Standard;
+
+var state = LuaState.Create();
+
+// 標準ライブラリを追加
+state.OpenStandardLibraries();
+
+var results = await state.DoStringAsync("return math.pi");
+Console.WriteLine(results[0]); // 3.141592653589793
+```
+
+標準ライブラリについては[Lua公式のマニュアル](https://www.lua.org/manual/5.2/manual.html#6)を参照してください。
+
+> [!WARNING]
+> Lua-CSharpは標準ライブラリの全ての関数をサポートしているわけではありません。詳細は[互換性](#互換性)の項目を参照してください。
+
+## 関数
+
+Luaの関数は`LuaFunction`型で表現されます。`LuaFunction`によってLuaの関数をC#側から呼び出したり、C#で定義した関数をLua側から呼び出したりすることが可能です。
+
+### Luaの関数をC#から呼び出す
+
+```lua
+-- lua2cs.lua
+
+local function add(a, b)
+    return a + b
+end
+
+return add;
+```
+
+```cs
+var results = await state.DoFileAsync("lua2cs.lua");
+var func = results[0].Read<LuaFunction>();
+
+// 引数を与えて関数を実行する
+var funcResults = await func.InvokeAsync(state, [1, 2]);
+
+// 3
+Console.WriteLine(funcResults[0]);
+```
+
+### C#の関数をLua側から呼び出す
+
+`LuaFunction.Create()`を用いてラムダ式からLuaFunctionを作成できます。
+
+```cs
+// グローバル環境に関数を追加
+state.Environment["add"] = LuaFunction.Create((args, ct) =>
+{
+    return [args[0].Read<double>() + ]
+});
+
+// Luaスクリプトを実行
+var results = await state.DoFileAsync("cs2lua.lua");
+
+// 3
+Console.WriteLine(results[i]);
+```
+
+```lua
+-- cs2lua.lua
+
+return add(1, 2)
+```
+
+ただし、`LuaFunction.Create()`で作成された関数は呼び出し時に余計なアロケーションを発生させます。最良のパフォーマンスを得るためには`LuaFunction`を継承したクラスを実装してください。
+
+## コルーチン
+
+Luaのコルーチンは`LuaThread`型で表現されます。
+
+コルーチンはLuaスクリプト内で利用できるだけでなく、Luaで作成したコルーチンをC#で待機することも可能です。
+
+```lua
+-- coroutine.lua
+
+local co = coroutine.create(function()
+    for i = 1, 10 do
+        print(i)
+        coroutine.yield()
+    end
+end)
+
+return co
+```
+
+```cs
+var results = await state.DoFileAsync("coroutine.lua");
+var co = results[0].Read<LuaThread>();
+
+for (int i = 0; i < 10; i++)
+{
+    var resumeResults = await co.ResumeAsync(state);
+
+    // coroutine.resume()と同様、成功時は最初の要素にtrue、それ以降に関数の戻り値を返す
+    // 1, 2, 3, 4, ...
+    Console.WriteLine(resumeResults[1]);
+}
+```
+
+## モジュールの読み込み
+
+Luaでは`require`関数を用いてモジュールを読み込むことができます。通常のLuaでは`package.searchers`の検索関数を用いてモジュールの管理を行いますが、Lua-CSharpでは代わりに`ILuaModuleLoader`がモジュール読み込みの機構として提供されています。
+
+```cs
+public interface ILuaModuleLoader
+{
+    bool Exists(string moduleName);
+    ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default);
+}
+```
+
+これを`LuaState.ModuleLoader`に設定することでモジュールの読み込み方法を変更することができます。デフォルトのLoaderにはluaファイルからモジュールをロードする`FileModuleLoader`が設定されています。
+
+また、`CompositeModuleLoader.Create(loader1, loader2, ...)`を利用することで複数のLoaderを組み合わせたLoaderを作成できます。
+
+```cs
+state.ModuleLoader = CompositeModuleLoader.Create(
+    new FileModuleLoader(),
+    new CustomModuleLoader()
+);
+```
+
+また、ロード済みのモジュールは通常のLua同様に`package.loaded`テーブルにキャッシュされます。これは`LuaState.LoadedModules`からアクセスすることが可能です。
+
+## 例外処理
+
+Luaスクリプトの解析エラーや実行時例外は`LuaException`を継承した例外をスローします。これをcatchすることでエラー時の処理を行うことができます。
+
+```cs
+try
+{
+    await state.DoFileAsync("filename.lua");
+}
+catch (LuaParseException)
+{
+    // 構文にエラーがあった際の処理
+}
+catch (LuaRuntimeException)
+{
+    // 実行時例外が発生した際の処理
+}
+```
+
+## 互換性
+
+Lua-CSharpは.NETとの統合を念頭に設計されているため、C実装とは互換性がない仕様がいくつか存在します。
+
+### バイナリ
+
+Lua-CSharpはLuaバイトコードをサポートしません(luacなどは使用できません)。実行可能なのはLuaソースコードのみです。
+
+### 文字コード
+
+Lua-CSharpで利用される文字コードはUTF-16です。通常Luaは1バイトで1文字を表すエンコーディングを前提としているため、文字列まわりの動作が大きく異なります。
+
+例えば、以下のコードの出力結果は通常のLuaでは`15`ですが、Lua-CSharpでは`5`です。
+
+```lua
+local l = string.len("あいうえお")
+print(l)
+```
+
+Stringライブラリの関数はすべて文字列をUTF-16として扱う実装に変更されていることに注意してください。
+
+### ガベージコレクション
+
+Lua-CSharpはC#で実装されているため.NETのGCに依存しています。そのため、メモリ管理に関する動作が通常のLuaとは異なります。
+
+`collectgarbage()`は利用可能ですが、これは単に`GC.Collect()`の呼び出しです。引数の値は無視されます。また、弱参照テーブル(week tables)はサポートされません。
+
+### moduleライブラリ
+
+moduleライブラリは`require()`および`package.loaded`のみが利用でき、それ以外の関数は実装されていません。これはLua-CSharpは.NETに最適化された独自のモジュール読み込みの機能を有するためです。
+
+詳細は[モジュールの読み込み](#モジュールの読み込み)の項目を参照してください。
+
+### debugライブラリ
+
+現在debugライブラリの実装は提供されていません。これはLua-CSharpの内部実装がC実装とは大きく異なり、同じAPIのライブラリを提供することが難しいためです。これについては、v1までに実装可能な一部のAPIのみの提供、または代替となるデバッグ機能を検討しています。
+
+## ライセンス
+
+このライブラリは[MITライセンス](LICENSE)の下で提供されています。