Prechádzať zdrojové kódy

Merge pull request #230 from nuskey8/readme

Update README.md/README_JA.md
Yusuke Nakada 1 mesiac pred
rodič
commit
088f66f153
3 zmenil súbory, kde vykonal 270 pridanie a 93 odobranie
  1. 130 36
      README.md
  2. 127 45
      README_JA.md
  3. 13 12
      src/Lua/Lua.csproj

+ 130 - 36
README.md

@@ -89,16 +89,17 @@ var isNil = results[0].Type == LuaValueType.Nil;
 
 
 Below is a table showing the type mapping between Lua and C#.
 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`       |
+| Lua               | C#                       |
+| ----------------- | ------------------------ |
+| `nil`             | `LuaValue.Nil`           |
+| `boolean`         | `bool`                   |
+| `string`          | `string`                 |
+| `number`          | `double`, `float`, `int` |
+| `table`           | `LuaTable`               |
+| `function`        | `LuaFunction`            |
+| (light)`userdata` | `object`                 |
+| `userdata`        | `ILuaUserData`           |
+| `thread`          | `LuaState`               |
 
 
 When creating a `LuaValue` from the C# side, compatible types are implicitly converted into `LuaValue`.
 When creating a `LuaValue` from the C# side, compatible types are implicitly converted into `LuaValue`.
 
 
@@ -182,33 +183,59 @@ return add;
 ```
 ```
 
 
 ```cs
 ```cs
+var state = LuaState.Create();
 var results = await state.DoFileAsync("lua2cs.lua");
 var results = await state.DoFileAsync("lua2cs.lua");
-var func = results[0].Read<LuaFunction>();
+var func = results[0];
 
 
-// Execute the function with arguments
-var funcResults = await func.InvokeAsync(state, new[] { 1, 2 });
+// Execute the function or any callable with arguments
+var funcResults = await state.CallAsync(func, [1, 2]);
 
 
 // 3
 // 3
 Console.WriteLine(funcResults[0]);
 Console.WriteLine(funcResults[0]);
 ```
 ```
 
 
+To avoid array allocation, an API is also provided that passes arguments using the stack.
+
+```cs
+var func = results[0];
+var basePos = state.Stack.Count;
+state.Push(func);
+state.Push(1);
+state.Push(2);
+var funcResultsCount = await state.CallAsync(funcIndex: basePos, returnBase: basePos);
+
+using (var reader = state.ReadStack(funcResultsCount))
+{
+    var span = reader.AsSpan();
+    for (int i = 0; i < span.Length; i++)
+    {
+        Console.WriteLine(span[i]);
+    }
+}
+```
+
 ### Calling C# Functions from Lua
 ### Calling C# Functions from Lua
 
 
 It is possible to create a `LuaFunction` from a lambda expression.
 It is possible to create a `LuaFunction` from a lambda expression.
 
 
 ```cs
 ```cs
 // Add the function to the global environment
 // Add the function to the global environment
-state.Environment["add"] = new LuaFunction((context, buffer, ct) =>
+state.Environment["add"] = new LuaFunction((context, ct) =>
 {
 {
     // Get the arguments using context.GetArgument<T>()
     // Get the arguments using context.GetArgument<T>()
     var arg0 = context.GetArgument<double>(0);
     var arg0 = context.GetArgument<double>(0);
     var arg1 = context.GetArgument<double>(1);
     var arg1 = context.GetArgument<double>(1);
 
 
-    // Write the return value to the buffer
-    buffer.Span[0] = arg0 + arg1;
+    // Set the return value to the context
+    context.Return(arg0 + arg1);
+    
+    // If there are multiple values, you need to pass them together as follows.
+    // context.Return(arg0, arg1);
+    // context.Return([arg0, arg1]);
 
 
     // Return the number of values
     // Return the number of values
     return new(1);
     return new(1);
+    // return new(context.Return(arg0 + arg1)); // or this way
 });
 });
 
 
 // Execute a Lua script
 // Execute a Lua script
@@ -227,17 +254,42 @@ return add(1, 2)
 > [!TIP]  
 > [!TIP]  
 > Defining functions with `LuaFunction` can be somewhat verbose. When adding multiple functions, it is recommended to use the Source Generator with the `[LuaObject]` attribute. For more details, see the [LuaObject](#luaobject) section.
 > Defining functions with `LuaFunction` can be somewhat verbose. When adding multiple functions, it is recommended to use the Source Generator with the `[LuaObject]` attribute. For more details, see the [LuaObject](#luaobject) section.
 
 
+## Low-Level API
+
+In addition to normal function calls, it is possible to directly call Lua's low-level API.
+
+```csharp
+await state.CallAsync(func, [arg1, arg2]); // func(arg1,arg2) in lua
+
+await state.AddAsync(arg1, arg2); // arg1 + arg2 in lua
+await state.SubAsync(arg1, arg2); // arg1 - arg2 in lua
+await state.MulAsync(arg1, arg2); // arg1 * arg2 in lua
+await state.DivAsync(arg1, arg2); // arg1 / arg2 in lua
+await state.ModAsync(arg1, arg2); // arg1 % arg2 in lua
+
+await state.EqualsAsync(arg1, arg2); // arg1 == arg2 in lua
+await state.LessThanAsync(arg1, arg2); // arg1 < arg2 in lua
+await state.LessThanOrEqualsAsync(arg1, arg2); // arg1 <= arg2 in lua
+
+await state.ConcatAsync([arg1, arg2, arg3]); // arg1 .. arg2 .. arg3 in lua
+
+await state.GetTableAsync(table, key); // table[key] in lua
+await state.GetTableAsync(table, "x"); // table.x in lua
+await state.SetTableAsync(table, key, value); // table[key] = value in lua
+await state.SetTableAsync(table, "x", value); // table.x = value in lua
+```
+
 ## Integration with async/await
 ## Integration with async/await
 
 
 `LuaFunction` operates asynchronously. Therefore, you can define a function that waits for an operation in Lua, such as the example below:
 `LuaFunction` operates asynchronously. Therefore, you can define a function that waits for an operation in Lua, such as the example below:
 
 
 ```cs
 ```cs
 // Define a function that waits for the given number of seconds using Task.Delay
 // Define a function that waits for the given number of seconds using Task.Delay
-state.Environment["wait"] = new LuaFunction(async (context, buffer, ct) =>
+state.Environment["wait"] = new LuaFunction(async (context, ct) =>
 {
 {
     var sec = context.GetArgument<double>(0);
     var sec = context.GetArgument<double>(0);
     await Task.Delay(TimeSpan.FromSeconds(sec));
     await Task.Delay(TimeSpan.FromSeconds(sec));
-    return 0;
+    return context.Return();
 });
 });
 
 
 await state.DoFileAsync("sample.lua");
 await state.DoFileAsync("sample.lua");
@@ -263,17 +315,16 @@ This code can resume the execution of the Lua script after waiting with await, a
 
 
 ## Coroutines
 ## Coroutines
 
 
-Lua coroutines are represented by the `LuaThread` type.
+Lua coroutines are represented by the `LuaState` type.
 
 
 Coroutines can not only be used within Lua scripts, but you can also await Lua-created coroutines from C#.
 Coroutines can not only be used within Lua scripts, but you can also await Lua-created coroutines from C#.
 
 
 ```lua
 ```lua
 -- coroutine.lua
 -- coroutine.lua
 
 
-local co = coroutine.create(function()
+ local co = coroutine.create(function()
     for i = 1, 10 do
     for i = 1, 10 do
-        print(i)
-        coroutine.yield()
+        print("lua:", coroutine.yield(i - 1))
     end
     end
 end)
 end)
 
 
@@ -282,15 +333,21 @@ return co
 
 
 ```cs
 ```cs
 var results = await state.DoFileAsync("coroutine.lua");
 var results = await state.DoFileAsync("coroutine.lua");
-var co = results[0].Read<LuaThread>();
-
+var co = results[0].Read<LuaState>();
+var stack = new LuaStack();
 for (int i = 0; i < 10; i++)
 for (int i = 0; i < 10; i++)
 {
 {
-    var resumeResults = await co.ResumeAsync(state);
+    var resumeResultsCount = await co.ResumeAsync(stack);
 
 
     // Similar to coroutine.resume(), returns true on success and the return values afterward
     // Similar to coroutine.resume(), returns true on success and the return values afterward
     // 1, 2, 3, 4, ...
     // 1, 2, 3, 4, ...
-    Console.WriteLine(resumeResults[1]);
+    if (resumeResultsCount > 1)
+    {
+        Console.WriteLine(stack[1]);
+    }
+
+    stack.Clear();
+    stack.Push(i);
 }
 }
 ```
 ```
 
 
@@ -436,7 +493,10 @@ print(v1 - v2) -- <-1, -1, -1>
 
 
 ## Module Loading
 ## 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.
+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 addition to this, Lua-CSharp provides `ILuaModuleLoader` as a module loading mechanism.
+
+> [!NOTE]
+> Module resolution by `ILuaModuleLoader` is performed before `package.searchers`.
 
 
 ```cs
 ```cs
 public interface ILuaModuleLoader
 public interface ILuaModuleLoader
@@ -446,29 +506,45 @@ public interface ILuaModuleLoader
 }
 }
 ```
 ```
 
 
-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 set the `LuaState.ModuleLoader` to change how modules are loaded.
 
 
 You can also combine multiple loaders using `CompositeModuleLoader.Create(loader1, loader2, ...)`.
 You can also combine multiple loaders using `CompositeModuleLoader.Create(loader1, loader2, ...)`.
 
 
 ```cs
 ```cs
 state.ModuleLoader = CompositeModuleLoader.Create(
 state.ModuleLoader = CompositeModuleLoader.Create(
-    new FileModuleLoader(),
-    new CustomModuleLoader()
+    new CustomModuleLoader1(),
+    new CustomModuleLoader2()
 );
 );
 ```
 ```
 
 
 Loaded modules are cached in the `package.loaded` table, just like regular Lua. This can be accessed via `LuaState.LoadedModules`.
 Loaded modules are cached in the `package.loaded` table, just like regular Lua. This can be accessed via `LuaState.LoadedModules`.
 
 
+## LuaPlatform
+
+In Lua-CSharp, environment abstraction is provided as `LuaPlatform` for sandboxing.
+
+```cs
+var platform = new LuaPlatform(
+    FileSystem: new FileSystem(),
+    OsEnvironment: new SystemOsEnvironment(),
+    StandardIO: new ConsoleStandardIO(),
+    TimeProvider: TimeProvider.System);
+
+var state = LuaState.Create(platform);
+```
+
+These are used for `require`, `print`, `dofile`, and the `os` module.
+
 ## Exception Handling
 ## Exception Handling
 
 
-Lua script parsing errors and runtime exceptions throw exceptions that inherit from `LuaException`. You can catch these to handle errors during execution.
+Lua script runtime exceptions throw exceptions that inherit from `LuaException`. You can catch these to handle errors during execution.
 
 
 ```cs
 ```cs
 try
 try
 {
 {
     await state.DoFileAsync("filename.lua");
     await state.DoFileAsync("filename.lua");
 }
 }
-catch (LuaParseException)
+catch (LuaCompileException)
 {
 {
     // Handle parsing errors
     // Handle parsing errors
 }
 }
@@ -476,6 +552,11 @@ catch (LuaRuntimeException)
 {
 {
     // Handle runtime exceptions
     // Handle runtime exceptions
 }
 }
+catch(OperationCanceledException)
+{
+    // Handle cancel exceptions
+    // LuaCanceledException allows you to get the cancellation point within Lua.
+}
 ```
 ```
 
 
 ## Lua.Unity
 ## Lua.Unity
@@ -523,13 +604,26 @@ state.ModuleLoader = new ResourcesModuleLoader();
 state.ModuleLoader = new AddressablesModuleLoader();
 state.ModuleLoader = new AddressablesModuleLoader();
 ```
 ```
 
 
-## Compatibility
+### UnityStandardIO / UnityApplicationOsEnvironment
 
 
-Lua-CSharp is designed with integration into .NET in mind, so there are several differences from the C implementation.
+`UnityStandardIO` and `UnityApplicationOsEnvironment` are provided as LuaPlatform elements for Unity.
+
+```cs
+var platform = new LuaPlatform(
+    FileSystem: new FileSystem(),
+    OsEnvironment: new UnityApplicationOsEnvironment(), // OsEnvironment for Unity
+    StandardIO: new UnityStandardIO(), // StandardIO for Unity
+    TimeProvider: TimeProvider.System);
+var state = LuaState.Create(platform);
+```
+
+With `UnityStandardIO`, standard output such as `print` will be output to `Debug.Log()`. With `UnityApplicationOsEnvironment`, environment variables can be set using a Dictionary, and `os.exit()` will call `Application.Quit()`.
 
 
-### Binary
+These are simple implementations only, so for actual use it is recommended creating your own implementations as needed.
 
 
-Lua-CSharp does not support Lua bytecode (tools like `luac` cannot be used). Only Lua source code can be executed.
+## Compatibility
+
+Lua-CSharp is designed with integration into .NET in mind, so there are several differences from the C implementation.
 
 
 ### Character Encoding
 ### Character Encoding
 
 

+ 127 - 45
README_JA.md

@@ -89,16 +89,17 @@ var isNil = results[0].Type == LuaValueType.Nil;
 
 
 Lua-C#間の型の対応を以下に示します。
 Lua-C#間の型の対応を以下に示します。
 
 
-| Lua        | C#               |
-| ---------- | ---------------- |
-| `nil`      | `LuaValue.Nil`   |
-| `boolean`  | `bool`           |
-| `string`   | `string`         |
-| `number`   | `double`,`float` |
-| `table`    | `LuaTable`       |
-| `function` | `LuaFunction`    |
-| `userdata` | `LuaUserData`    |
-| `thread`   | `LuaThread`      |
+| Lua               | C#                       |
+| ----------------- | ------------------------ |
+| `nil`             | `LuaValue.Nil`           |
+| `boolean`         | `bool`                   |
+| `string`          | `string`                 |
+| `number`          | `double`, `float`, `int` |
+| `table`           | `LuaTable`               |
+| `function`        | `LuaFunction`            |
+| (light)`userdata` | `object`                 |
+| `userdata`        | `ILuaUserData`           |
+| `thread`          | `LuaState`               |
 
 
 C#側から`LuaValue`を作成する際には、変換可能な型の場合であれば暗黙的に`LuaValue`に変換されます。
 C#側から`LuaValue`を作成する際には、変換可能な型の場合であれば暗黙的に`LuaValue`に変換されます。
 
 
@@ -115,14 +116,14 @@ Luaのテーブルは`LuaTable`型で表現されます。これは通常の`Lua
 
 
 ```cs
 ```cs
 // Lua側でテーブルを作成
 // Lua側でテーブルを作成
-var results = await state.DoStringAsync("return { a = 1, b = 2, c = 3 }")
+var results = await state.DoStringAsync("return { a = 1, b = 2, c = 3 }");
 var table1 = results[0].Read<LuaTable>();
 var table1 = results[0].Read<LuaTable>();
 
 
 // 1
 // 1
 Console.WriteLine(table1["a"]);
 Console.WriteLine(table1["a"]);
 
 
 // テーブルを作成
 // テーブルを作成
-results = await state.DoStringAsync("return { 1, 2, 3 }")
+results = await state.DoStringAsync("return { 1, 2, 3 }");
 var table2 = results[0].Read<LuaTable>();
 var table2 = results[0].Read<LuaTable>();
 
 
 // 1 (Luaの配列は1-originであることに注意)
 // 1 (Luaの配列は1-originであることに注意)
@@ -183,32 +184,55 @@ return add;
 
 
 ```cs
 ```cs
 var results = await state.DoFileAsync("lua2cs.lua");
 var results = await state.DoFileAsync("lua2cs.lua");
-var func = results[0].Read<LuaFunction>();
+var func = results[0];
 
 
 // 引数を与えて関数を実行する
 // 引数を与えて関数を実行する
-var funcResults = await func.InvokeAsync(state, [1, 2]);
+var funcResults = await state.CallAsync(func, [1, 2]);
 
 
 // 3
 // 3
 Console.WriteLine(funcResults[0]);
 Console.WriteLine(funcResults[0]);
 ```
 ```
 
 
+配列のアロケーションを回避するために、スタックを用いて引数をやり取りするAPIも提供されています。
+
+```cs
+var func = results[0];
+var basePos = state.Stack.Count;
+state.Push(func);
+state.Push(1);
+state.Push(2);
+var funcResultsCount = await state.CallAsync(funcIndex: basePos, returnBase: basePos);
+using (var reader = state.ReadStack(funcResultsCount))
+{
+    var span = reader.AsSpan();
+    for (int i = 0; i < span.Length; i++)
+    {
+        Console.WriteLine(span[i]);
+    }
+}
+```
+
 ### C#の関数をLua側から呼び出す
 ### C#の関数をLua側から呼び出す
 
 
 ラムダ式からLuaFunctionを作成することが可能です。
 ラムダ式からLuaFunctionを作成することが可能です。
 
 
 ```cs
 ```cs
 // グローバル環境に関数を追加
 // グローバル環境に関数を追加
-state.Environment["add"] = new LuaFunction((context, buffer, ct) =>
+state.Environment["add"] = new LuaFunction((context, ct) =>
 {
 {
     // context.GetArgument<T>()で引数を取得
     // context.GetArgument<T>()で引数を取得
     var arg0 = context.GetArgument<double>(0);
     var arg0 = context.GetArgument<double>(0);
     var arg1 = context.GetArgument<double>(1);
     var arg1 = context.GetArgument<double>(1);
 
 
-    // バッファに戻り値を記録
-    buffer.Span[0] = arg0 + arg1;
+    // context.Return()で戻り値を渡す
+    var count = context.Return(arg0 + arg1);
+    
+    // 複数の戻り値を渡すことも可能
+    // context.Return(arg0, arg1);
+    // context.Return([arg0, arg1]);
 
 
     // 戻り値の数を返す
     // 戻り値の数を返す
-    return new(1);
+    return new(count);
 });
 });
 
 
 // Luaスクリプトを実行
 // Luaスクリプトを実行
@@ -227,17 +251,42 @@ return add(1, 2)
 > [!TIP]
 > [!TIP]
 > `LuaFunction`による関数の定義はやや記述量が多いため、関数をまとめて追加する際には`[LuaObject]`属性によるSource Generatorの使用を推奨します。詳細は[LuaObject](#luaobject)の項目を参照してください。
 > `LuaFunction`による関数の定義はやや記述量が多いため、関数をまとめて追加する際には`[LuaObject]`属性によるSource Generatorの使用を推奨します。詳細は[LuaObject](#luaobject)の項目を参照してください。
 
 
+## 低レベルAPI
+
+通常の関数呼び出し以外にも、Luaの低レベルなAPIを直接呼び出すことが可能です。
+
+```csharp
+await state.CallAsync(func, [arg1, arg2]); // func(arg1,arg2) in lua
+
+await state.AddAsync(arg1, arg2); // arg1 + arg2 in lua
+await state.SubAsync(arg1, arg2); // arg1 - arg2 in lua
+await state.MulAsync(arg1, arg2); // arg1 * arg2 in lua
+await state.DivAsync(arg1, arg2); // arg1 / arg2 in lua
+await state.ModAsync(arg1, arg2); // arg1 % arg2 in lua
+
+await state.EqualsAsync(arg1, arg2); // arg1 == arg2 in lua
+await state.LessThanAsync(arg1, arg2); // arg1 < arg2 in lua
+await state.LessThanOrEqualsAsync(arg1, arg2); // arg1 <= arg2 in lua
+
+await state.ConcatAsync([arg1, arg2, arg3]); // arg1 .. arg2 .. arg3 in lua
+
+await state.GetTableAsync(table, key); // table[key] in lua
+await state.GetTableAsync(table, "x"); // table.x in lua
+await state.SetTableAsync(table, key, value); // table[key] = value in lua
+await state.SetTableAsync(table, "x", value); // table.x = value in lua
+```
+
 ## async/awaitとの統合
 ## async/awaitとの統合
 
 
 `LuaFunction`は非同期メソッドとして動作します。そのため、以下のような関数を定義することでLua側から処理の待機を行うことも可能です。
 `LuaFunction`は非同期メソッドとして動作します。そのため、以下のような関数を定義することでLua側から処理の待機を行うことも可能です。
 
 
 ```cs
 ```cs
 // 与えられた秒数だけTask.Delayで待機する関数を定義
 // 与えられた秒数だけTask.Delayで待機する関数を定義
-state.Environment["wait"] = new LuaFunction(async (context, buffer, ct) =>
+state.Environment["wait"] = new LuaFunction(async (context, ct) =>
 {
 {
     var sec = context.GetArgument<double>(0);
     var sec = context.GetArgument<double>(0);
     await Task.Delay(TimeSpan.FromSeconds(sec));
     await Task.Delay(TimeSpan.FromSeconds(sec));
-    return 0;
+    return context.Return();
 });
 });
 
 
 await state.DoFileAsync("sample.lua");
 await state.DoFileAsync("sample.lua");
@@ -263,17 +312,16 @@ print "goodbye!"
 
 
 ## コルーチン
 ## コルーチン
 
 
-Luaのコルーチンは`LuaThread`型で表現されます。
+Luaのコルーチンは`LuaState`型で表現されます。
 
 
 コルーチンはLuaスクリプト内で利用できるだけでなく、Luaで作成したコルーチンをC#で待機することも可能です。
 コルーチンはLuaスクリプト内で利用できるだけでなく、Luaで作成したコルーチンをC#で待機することも可能です。
 
 
 ```lua
 ```lua
 -- coroutine.lua
 -- coroutine.lua
 
 
-local co = coroutine.create(function()
+ local co = coroutine.create(function()
     for i = 1, 10 do
     for i = 1, 10 do
-        print(i)
-        coroutine.yield()
+        print("lua:", coroutine.yield(i))
     end
     end
 end)
 end)
 
 
@@ -283,14 +331,21 @@ return co
 ```cs
 ```cs
 var results = await state.DoFileAsync("coroutine.lua");
 var results = await state.DoFileAsync("coroutine.lua");
 var co = results[0].Read<LuaThread>();
 var co = results[0].Read<LuaThread>();
+var stack = new LuaStack();
 
 
 for (int i = 0; i < 10; i++)
 for (int i = 0; i < 10; i++)
 {
 {
-    var resumeResults = await co.ResumeAsync(state);
+    var resumeResultsCount = await co.ResumeAsync(stack);
 
 
     // coroutine.resume()と同様、成功時は最初の要素にtrue、それ以降に関数の戻り値を返す
     // coroutine.resume()と同様、成功時は最初の要素にtrue、それ以降に関数の戻り値を返す
     // 1, 2, 3, 4, ...
     // 1, 2, 3, 4, ...
-    Console.WriteLine(resumeResults[1]);
+    if (resumeResultsCount > 1)
+    {
+        Console.WriteLine(stack[1]);
+    }
+
+    stack.Clear();
+    stack.Push(i);
 }
 }
 ```
 ```
 
 
@@ -436,7 +491,10 @@ print(v1 - v2) -- <-1, -1, -1>
 
 
 ## モジュールの読み込み
 ## モジュールの読み込み
 
 
-Luaでは`require`関数を用いてモジュールを読み込むことができます。通常のLuaでは`package.searchers`の検索関数を用いてモジュールの管理を行いますが、Lua-CSharpでは代わりに`ILuaModuleLoader`がモジュール読み込みの機構として提供されています。
+Luaでは`require`関数を用いてモジュールを読み込むことができます。通常のLuaでは`package.searchers`の検索関数を用いてモジュールの管理を行いますが、Lua-CSharpではそれに加えて`ILuaModuleLoader`がモジュール読み込みの機構として提供されています。
+
+> [!NOTE]
+> `ILuaModuleLoader`によるモジュールの解決は`package.searchers`より先に実行されます。
 
 
 ```cs
 ```cs
 public interface ILuaModuleLoader
 public interface ILuaModuleLoader
@@ -452,23 +510,39 @@ public interface ILuaModuleLoader
 
 
 ```cs
 ```cs
 state.ModuleLoader = CompositeModuleLoader.Create(
 state.ModuleLoader = CompositeModuleLoader.Create(
-    new FileModuleLoader(),
-    new CustomModuleLoader()
+    new CustomModuleLoader1(),
+    new CustomModuleLoader2()
 );
 );
 ```
 ```
 
 
 また、ロード済みのモジュールは通常のLua同様に`package.loaded`テーブルにキャッシュされます。これは`LuaState.LoadedModules`からアクセスすることが可能です。
 また、ロード済みのモジュールは通常のLua同様に`package.loaded`テーブルにキャッシュされます。これは`LuaState.LoadedModules`からアクセスすることが可能です。
 
 
+## LuaPlatform
+
+Lua-CSharpではサンドボックス化のための機能として、スクリプト環境の抽象化を`LuaPlatform`として提供しています。
+
+```cs
+var platform = new LuaPlatform(
+    FileSystem: new FileSystem(),
+    OsEnvironment: new SystemOsEnvironment(),
+    StandardIO: new ConsoleStandardIO(),
+    TimeProvider: TimeProvider.System);
+
+var state = LuaState.Create(platform);
+```
+
+これらは`require`, `print`, `dofile`や`os`モジュールなどで使用されます。
+
 ## 例外処理
 ## 例外処理
 
 
-Luaスクリプトの解析エラーや実行時例外は`LuaException`を継承した例外をスローします。これをcatchすることでエラー時の処理を行うことができます。
+Luaスクリプトの実行時例外は`LuaRuntimeException`を継承した例外をスローします。これをcatchすることでエラー時の処理を行うことができます。
 
 
 ```cs
 ```cs
 try
 try
 {
 {
     await state.DoFileAsync("filename.lua");
     await state.DoFileAsync("filename.lua");
 }
 }
-catch (LuaParseException)
+catch (LuaCompileException)
 {
 {
     // 構文にエラーがあった際の処理
     // 構文にエラーがあった際の処理
 }
 }
@@ -476,6 +550,11 @@ catch (LuaRuntimeException)
 {
 {
     // 実行時例外が発生した際の処理
     // 実行時例外が発生した際の処理
 }
 }
+catch (OperationCanceledException)
+{
+    // キャンセルが発生した際の処理
+    // LuaCanceledExceptionを代わりに用いることで、Luaスクリプト内部でのキャンセル位置を取得可能
+}
 ```
 ```
 
 
 ## Lua.Unity
 ## Lua.Unity
@@ -523,13 +602,26 @@ state.ModuleLoader = new ResourcesModuleLoader();
 state.ModuleLoader = new AddressablesModuleLoader();
 state.ModuleLoader = new AddressablesModuleLoader();
 ```
 ```
 
 
-## 互換性
+### UnityStandardIO / UnityApplicationOsEnvironment
 
 
-Lua-CSharpは.NETとの統合を念頭に設計されているため、C実装とは互換性がない仕様がいくつか存在します。
+Unity向けのLuaPlatformの要素として`UnityStandardIO`、`UnityApplicationOsEnvironment`が提供されています。
+
+```cs
+var platform = new LuaPlatform(
+    FileSystem: new FileSystem(),
+    OsEnvironment: new UnityApplicationOsEnvironment(), // Unity向けのOsEnvironment
+    StandardIO: new UnityStandardIO(), // Unity向けのStandardIO
+    TimeProvider: TimeProvider.System);
+var state = LuaState.Create(platform);
+```
+
+`UnityStandardIO`では`print`などの標準出力が`Debug.Log()`に出力されるようになります。`UnityApplicationOsEnvironment`では環境変数をDictionaryで設定することが可能となり、`os.exit()`で`Application.Quit()`が呼ばれるようになります。
 
 
-### バイナリ
+これらはあくまで簡易的な実装であるため、実運用では必要に応じて独自実装を作成することを推奨します。
 
 
-Lua-CSharpはLuaバイトコードをサポートしません(luacなどは使用できません)。実行可能なのはLuaソースコードのみです。
+## 互換性
+
+Lua-CSharpは.NETとの統合を念頭に設計されているため、C実装とは互換性がない仕様がいくつか存在します。
 
 
 ### 文字コード
 ### 文字コード
 
 
@@ -550,16 +642,6 @@ Lua-CSharpはC#で実装されているため.NETのGCに依存しています
 
 
 `collectgarbage()`は利用可能ですが、これは単に`GC.Collect()`の呼び出しです。引数の値は無視されます。また、弱参照テーブル(week tables)はサポートされません。
 `collectgarbage()`は利用可能ですが、これは単に`GC.Collect()`の呼び出しです。引数の値は無視されます。また、弱参照テーブル(week tables)はサポートされません。
 
 
-### moduleライブラリ
-
-moduleライブラリは`require()`および`package.loaded`のみが利用でき、それ以外の関数は実装されていません。これはLua-CSharpは.NETに最適化された独自のモジュール読み込みの機能を有するためです。
-
-詳細は[モジュールの読み込み](#モジュールの読み込み)の項目を参照してください。
-
-### debugライブラリ
-
-現在debugライブラリの実装は提供されていません。これはLua-CSharpの内部実装がC実装とは大きく異なり、同じAPIのライブラリを提供することが難しいためです。これについては、v1までに実装可能な一部のAPIのみの提供、または代替となるデバッグ機能を検討しています。
-
 ## ライセンス
 ## ライセンス
 
 
 このライブラリは[MITライセンス](LICENSE)の下で提供されています。
 このライブラリは[MITライセンス](LICENSE)の下で提供されています。

+ 13 - 12
src/Lua/Lua.csproj

@@ -19,28 +19,29 @@
             <PrivateAssets>all</PrivateAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
         </PackageReference>
 
 
-        <None Include="..\Lua.SourceGenerator\bin\$(Configuration)\netstandard2.0\Lua.SourceGenerator.dll"
-              PackagePath="analyzers\dotnet\cs"
-              Pack="true"
-              Visible="false"/>
+        <None
+            Include="..\Lua.SourceGenerator\bin\$(Configuration)\netstandard2.0\Lua.SourceGenerator.dll"
+            PackagePath="analyzers\dotnet\cs"
+            Pack="true"
+            Visible="false" />
     </ItemGroup>
     </ItemGroup>
     <ItemGroup Condition="$(TargetFramework) == 'net6.0' Or $(TargetFramework) == 'netstandard2.1'">
     <ItemGroup Condition="$(TargetFramework) == 'net6.0' Or $(TargetFramework) == 'netstandard2.1'">
-        <PackageReference Include="Microsoft.Bcl.TimeProvider" Version="8.0.0"/>
+        <PackageReference Include="Microsoft.Bcl.TimeProvider" Version="8.0.0" />
     </ItemGroup>
     </ItemGroup>
     <ItemGroup Condition="$(TargetFramework) == 'netstandard2.1'">
     <ItemGroup Condition="$(TargetFramework) == 'netstandard2.1'">
-        <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0"/>
+        <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
     </ItemGroup>
     </ItemGroup>
 
 
     <ItemGroup>
     <ItemGroup>
-        <None Include="../../Icon.png" Pack="true" PackagePath="/"/>
-        <None Include="..\..\README.md" Pack="true" PackagePath="README.md"/>
-        <EmbeddedResource Include="..\..\LICENSE"/>
+        <None Include="../../Icon.png" Pack="true" PackagePath="/" />
+        <None Include="..\..\README.md" Pack="true" PackagePath="README.md" />
+        <EmbeddedResource Include="..\..\LICENSE" />
     </ItemGroup>
     </ItemGroup>
 
 
     <ItemGroup>
     <ItemGroup>
         <ProjectReference Include="..\Lua.SourceGenerator\Lua.SourceGenerator.csproj"
         <ProjectReference Include="..\Lua.SourceGenerator\Lua.SourceGenerator.csproj"
-                          OutputItemType="Analyzer"
-                          ReferenceOutputAssembly="false"/>
+            OutputItemType="Analyzer"
+            ReferenceOutputAssembly="false" />
     </ItemGroup>
     </ItemGroup>
 
 
-</Project>
+</Project>