Skip to content

Latest commit

 

History

258 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TinyC# (tcs)

C# サブセットから Lua 5.5 ソースコードへのトランスパイラ。ゲームスクリプティング向け。

Roslyn で C# を解析し、Lua のテーブル + メタテーブル OOP に変換する。 型チェックは C# コンパイラが行い、Lua 側は型消去で動作する。

必要なもの

  • .NET 10 SDK (版は global.json で固定。導入は dotnet-install.sh --jsonfile global.json を推奨 — distro package は workload と版がずれるため混ぜない)
  • CMake 3.12+ (Lua ビルド用)
  • C コンパイラ (MSVC / GCC / Clang)

セットアップ

git clone --recursive https://github.com/neguse/tcs.git
cd tcs

サブモジュールを取得し忘れた場合:

git submodule update --init --recursive

deps/lua は Lua 5.5 ソースの git submodule として固定する。 更新する場合は submodule commit を明示的に進め、deps/lua/lua -vLua 5.5 で始まることを run-tests で確認する。

Lua 5.5 のビルド

CMake でクロスプラットフォームビルド:

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release

Windows (MSVC) の場合も同じ。Visual Studio の Developer Command Prompt か PowerShell で実行する。 CMake は Linux / Windows / macOS / iOS-family / Emscripten / BSD / generic Unix で Lua の compile definitions と system libs を分岐する。 ビルド成果物は deps/lua/lua (Unix) または deps/lua/lua.exe (Windows) に出力される。

テスト実行

dotnet test

Linux:

./run-tests.sh

Windows (PowerShell):

.\run-tests.ps1

Lua が未ビルド、CMake 入力より古い、または Lua 5.5 ではない場合は自動で cmake を呼んでビルドしてからテストを実行する。 テスト時は deps/lua/lua -vLua 5.5 で始まることも検証する。 run-testsdotnet test に加え、代表 sample の tcs check、Rider helper script、analyzer-demo build の expected diagnostics、analyzer nupkg の PackageReference consumer と .editorconfig severity override を検査する。 GitHub Actions (.github/workflows/ci.yml) でも同じ gate を実行する。

依存と配布

NuGet package は floating version を使わず .csproj に明示 version を pin し、packages.lock.json で transitive dependency も固定する。 依存を更新する場合は対象 package version と lock file を同時に更新する。

CLI publish:

dotnet publish Transpiler/Transpiler.csproj -c Release -o publish/tcs

publish 出力には runtime/tinysystem.luaruntime/ 配下に同梱される。 通常の CLI 出力はこの runtime を読み込んで Lua prelude に埋め込むため、publish ディレクトリ単体で変換できる。

使い方

基本: C# → Lua 変換

# ヘルプ / バージョン
dotnet run --project Transpiler -- --help
dotnet run --project Transpiler -- --version

# stdout に出力
dotnet run --project Transpiler -- samples/hello.cs

# ファイルに出力
dotnet run --project Transpiler -- samples/hello.cs -o out.lua

-oの出力と--sourcemapのmapは、input / --ref / --preludeと 同じパスにはできない。Windows / Linux / macOSではsymlinkやhardlink等で 同じ実体を指す場合も含め、衝突時は書き込み前にエラーとなり、入力を変更しない。 衝突しない既存の出力linkはlink先へ書かず、出力entryを通常ファイルへ置換する。 既存出力が書き込み不可なら従来どおりエラーにし、Linux / macOSでは置換前の Unix permission bitsを維持する。

CLI 生成 Lua はデフォルトで TinySystem runtime prelude を埋め込む。 List / Dict / Math / String / Random は生成物だけで利用できる。 エンジン側で runtime を供給する場合は --no-runtime を付ける。

dotnet run --project Transpiler -- samples/hello.cs -o out.lua --no-runtime

--entry <Class> を付けると出力末尾に emitted 名の return を追記し、 require / dofile が class table を返す Lua module として使える (host が module の callback を呼ぶ engine 組み込み向け)。 指定は metadata 名 (Game.App) と一意な simple 名 (App) の両方を解決し、 interface / --ref 型 / 曖昧な simple 名はエラーになる。 --module を付けると出力末尾に定義した型 (--ref を除く) の table を返す return { Counter = Counter, ... } を追記し、ライブラリを local m = require("lib") で読む Lua module として使える (--entry / --snapshot とは併用しない)。 --prelude <shim.lua> は任意のユーザー Lua (host API を tcs stub の形に 橋渡しする shim など) を出力の先頭に前置する。

output / source map のパスが入力・--ref--prelude と同じ実体 (symlink / hardlink 含む) になる場合は、書き込み前にエラーにして 入力破壊を防ぐ (watch の各 rebuild でも再検証)。

dotnet run --project Transpiler -- game.cs -o game.lua --entry Game --prelude engine-shim.lua

lub エンジンで動かす実例は samples/lub/ (run-lub.sh) と doc/lub-gap-analysis.md を参照。

準拠チェック

Lua を出力せず、C# compile error と TinyC# 準拠診断だけを返す。 警告またはエラーがあれば exit 1、問題がなければ exit 0。 TinyC#固有の例外はenumと数値整数 (charを除く) の変換・等値比較、および 互換public fieldによるinterface property facadeだけで、同じC#エラーIDの 通常の型不一致は失敗する。 Lua 出力のメンバ名は C# 名から規則で写す (BeginPassbegin_pass、enum メンバ DontCareDONT_CARE--ref 型の static アクセスは Lub.Gfxlub.gfx)。host の wire format が snake_case なら C# 側は通常の naming convention で書ける (規則は doc/support-matrix.md の 「Lua 出力の名前規則」)。--no-naming-check は naming convention warning だけを抑制する。

dotnet run --project Transpiler -- check samples/hello.cs
dotnet run --project Transpiler -- check game.cs --ref engine-stub.cs

複数ファイル

dotnet run --project Transpiler -- src/Player.cs src/Enemy.cs src/Game.cs -o game.lua

同一 Compilation でコンパイルするので、ファイル間のクラス参照が解決される。

参照専用 stub / facade

Lua に出力しない型チェック専用ファイルは --ref で渡す。 外部エンジン API や host 提供 API の最小 stub を置く用途。

dotnet run --project Transpiler -- game.cs --ref engine-stub.cs -o game.lua

この repo では engine 固有名に依存しない例として、 samples/host_api_game.cssamples/host_api_stub.cs を用意している。

dotnet run --project Transpiler -- samples/host_api_game.cs --ref samples/host_api_stub.cs -o host_api_game.lua

ソースマップ

dotnet run --project Transpiler -- game.cs -o game.lua --sourcemap

game.lua.map が生成される。Lua 行番号 → C# ファイル:行番号 の JSON マッピング。 runtime prelude を埋め込む通常出力でも、map の Lua 行番号は生成された .lua ファイル上の行番号に合わせて offset 済み。

.lua.map の形式:

{
  "version": 1,
  "mappings": {
    "42": {"file": "game.cs", "line": 12}
  }
}

Lua runtime error の stack trace は SourceMap で注釈できる:

deps/lua/lua game.lua 2> trace.txt
dotnet run --project Transpiler -- --map-stacktrace game.lua.map trace.txt

trace.txt を省略した場合は stdin から読む。SourceMap の exact 行が無い場合は直前の mapping を使う。

watch モード

dotnet run --project Transpiler -- src/*.cs -o out.lua --watch

src/*.cs は shell が展開する例。shell が glob 展開しない環境ではファイルを個別に渡す。 ファイル変更を検知して自動で再トランスパイルする。Ctrl+C で停止。

tcs analyzer PoC

Rider などの C# IDE 上で tcs 非準拠コードを警告するための Roslyn Analyzer PoC。 現時点では struct, record struct, partial 型, lock, nameof, try/catch, throw, local function, list pattern、未対応 BCL API / 未対応 core library member、collection への null 保存を TCS1001 / TCS1002 / TCS1003 として報告する。 同じ共有ルールを tcs check と transpiler warning でも使う。

dotnet test TinyCs.Analyzers.Tests
dotnet build samples/analyzer-demo/analyzer-demo.csproj

demo project 固有の expected diagnostics と Rider 確認手順は samples/analyzer-demo/README.md にも残している。 run-tests は analyzer nupkg を pack し、一時 project から PackageReference で参照して同じ診断が出ることと、.editorconfigTCS1001 / TCS1002 / TCS1003 を error にした build が失敗することも検証する。 samples/analyzer-demo/verify-inspectcode.sh で JetBrains InspectCode 2026.1.3 の headless 実行でも、ProjectReference と local nupkg PackageReference consumer の両方で TCS1001 x5 / TCS1002 x1 / TCS1003 x1 が SARIF に出ることを確認できる。さらに PackageReference consumer の .editorconfigTCS1001 / TCS1002 / TCS1003 を error にした場合、InspectCode が同じ件数の error を返すことも検証する。

通常の C# project から package として参照する場合:

dotnet pack TinyCs.Analyzers/TinyCs.Analyzers.csproj -c Release -o .nupkgs
dotnet restore your-project.csproj --source .nupkgs --source https://api.nuget.org/v3/index.json
<PackageReference Include="TinyCs.Analyzers"
                  Version="0.1.0"
                  PrivateAssets="all" />

analyzer package の release 手順

NuGet 公開は当面行わず、local nupkg の配布のみとする。

  1. TinyCs.Analyzers/TinyCs.Analyzers.csprojPackageVersion を上げる
  2. run-tests.sh 内の consumer 検証と本 README の PackageReference 例の Version を同じ値に更新する
  3. dotnet pack TinyCs.Analyzers/TinyCs.Analyzers.csproj -c Release -o .nupkgs で再生成する
  4. bash run-tests.sh を実行し、nupkg consumer build と .editorconfig severity override の検証が通ることを確認する

repository 内で直接参照する場合:

<ProjectReference Include="..\TinyCs.Analyzers\TinyCs.Analyzers.csproj"
                  OutputItemType="Analyzer"
                  ReferenceOutputAssembly="false"
                  PrivateAssets="all" />

.editorconfig で重要度を変更できる:

dotnet_diagnostic.TCS1001.severity = warning
dotnet_diagnostic.TCS1002.severity = error
dotnet_diagnostic.TCS1003.severity = warning

Rider 実機確認の手順:

  1. 必要なら samples/analyzer-demo/open-rider-demo.sh で pre-check 後に demo project を Rider で開く。Windows では .\samples\analyzer-demo\open-rider-demo.ps1 を使う。Rider command を自動検出できない場合は TCS_RIDER_COMMAND=/path/to/rider.sh、Windows では $env:TCS_RIDER_COMMAND = "C:\path\to\rider64.exe" を指定する
  2. samples/analyzer-demo/analyzer-demo.csproj を含む solution/project を Rider で開く
  3. Restore 後、samples/analyzer-demo/Program.cs を開く
  4. struct, local function, try, throw, values is [1, 2], System.IO.File.ReadAllText, List<string?> { null } に Roslyn inspection の squiggle が出ることを確認する
  5. Build tool window で TCS1001 が5件、TCS1002 が1件、TCS1003 が1件出ることを確認する
  6. .editorconfigdotnet_diagnostic.TCS1001/TCS1002/TCS1003.severityerror へ変え、Rider 表示が追従するか確認する(build 上の severity override は run-tests で検証済み)
  7. samples/analyzer-demo/RIDER_VERIFICATION_TEMPLATE.md に沿って、結果を q.md に go / no-go として記録する

サポートしている C# 機能

class, enum, interface(型チェックのみ), 継承, コンストラクタ, auto/custom プロパティ, static/instance メソッド, if/else/while/for/foreach/switch, ラムダ, List<T>, Dictionary<K,V>, LINQ (Where/Select/Any/All/First/Last/OrderBy/OrderByDescending/Take/Skip/Min/Max/Sum/Count/ToDictionary), 文字列補間, null 条件演算子 (?.), is パターン, switch 式 など。

詳細は doc/support-matrix.md 参照。

プロジェクト構成

tcs/
  Transpiler/          # トランスパイラ本体 (Roslyn → Lua)
  Transpiler.Tests/    # xUnit テスト
  TinyCs.Analyzers/    # Roslyn Analyzer PoC
  TinyCs.Analyzers.Tests/
  TinySystem/          # C# 側の型定義/facade (コンパイル・補完用)
  runtime/
    tinysystem.lua     # Lua ランタイムライブラリ (List/Dict/String/Math)
  deps/
    lua/               # Lua 5.5 ソース (git submodule)
  samples/             # サンプル C# コード
  doc/
    support-matrix.md  # サポートマトリクス
  CMakeLists.txt       # Lua クロスプラットフォームビルド

ライセンス

MIT

About

tcs: A C# subset implementation that runs on a Lua runtime.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages