公開 · 技術詳細解説

AS/400(IBM i)モダナイゼーション ― SlimeCL / SlimeRPG でできること・できないこと

正直な、実機 corpus に基づく解説です。以下の数字は実在する公開 IBM i ソース由来で、都合よく選んだ demo ではありません。できない所も公開します ― 透明性こそが moat だからです(競合は multi-target の合格率自体ほとんど公表しません)。

論旨。 CL は IBM i 現場の「糊」、RPG / COBOL は業務ロジック。これらを可搬な bash単一バイナリ Rust に変換します ― 「移行」ではなく「継続」。本ページは、それが今どこまで動くかを、hang や compile の穴も含めて計測値で示します。

実機ソースで計測(demo ではなく)

1,711
実機 RPG プログラム投入(jariko / OSSILE / noxDB / IBM サンプル)
46
実機 CL プログラム ― 公開「IBM i RPG Free CLP」corpus(.clp / .clle)
0
hang / crash(上記全体。最初に見つけた 10 件の hang は修正済み)

RPG(SlimeRPG → Java/runtime)

段階結果読み方
字句解析(lex)99.4%(1,701 / 1,711)固定形式 RPG/400 含め実機 RPG をほぼ全部読める
emit(出力生成)99.8%crash せず構造化変換
javac compile(標本)約82%正直な穴 ― 周辺 RPG(BIF・cursor 項目)が未完
hang0 / 1,711emitter 無限ループ 10 件を発見・修正(DO-UNTIL / MONITOR / inline データ構造)

CL(SlimeCL → bash / Rust)

段階結果読み方
字句解析(lex)100%(46 / 46)実機 CL を全部読める
emit100%(46 / 46)、hang 0固まらない・落ちない
bash 構文OK100%(46 / 46)CL のコマンド連携的な性質に bash が最も自然
Rust compile100%(46 / 46)この公開 corpus の全プログラムが target に compile

本イテレーション(2026-05-26)、同一 corpus で Rust compile は 69.6% → 100%、bash は 76.1% → 100% に上昇。shim 追加ではなく(未対応コマンドは元々コメントとして compile する)、古い CL の方言 ― 位置パラメータ形式(CHGVAR &X '1'DCL (&X) (*CHAR)・位置形式 IF/THEN)、予約語の変数名(&Type)、部分文字列代入(CHGVAR %SST(&v s l))、記号比較演算子(= > <)、暗黙 DCLF *CHAR フィールド%BIN、特殊値(*YES)、*CHAR*DEC 双方向変換、Rust の所有権(借用 vs move)、そして DDS 前段(DCLF の record フィールド型を .DSPF/.PF 定義から直接読む ― BMS / XMAP map parser と同じ positional 抽出の発想)― への emitter 対応による。各修正は無回帰を確認(29 unit tests + 4/4 bit-exact)。bitexact/measure_compile.sh で再現可能。

実機 IBM i で bit-exact 検証 ✓ 新

原 CL を実機 IBM i(7.5、PUB400.com)でコンパイル・実行し、その出力を SlimeCL が吐いた bash Rust の出力とバイト単位で比較しました。これまで「未公開」としていたマイルストーンです。達成しました。

6 / 6 bit-exact(3者一致)。 下記すべてで 実機 IBM i == SlimeCL bash == SlimeCL Rust がバイト単位で一致。
プログラム実機 IBM iSlimeCL bashSlimeCL Rust
countdown(1..5 の和、*DEC*CHAR)000000000000000000150000000000000000001500000000000000000015PASS
exprdemo(括弧・*AND*CAT)OKOKOKPASS
sstdemo(%SST 部分文字列)HELLOHELLOHELLOPASS
dtaara(データエリア + ALCOBJ/DLCOBJ)HELLOHELLOHELLOPASS
ebcdic('A' *LT 'a' ― EBCDIC 照合)GEGEGEPASS
binconv(%BIN('//') ― big-endian *INT2)…24929…24929…24929PASS

後の2本は compile は通るが実機で裏で揺れる Runtime Gap を封じたもの:*CHAR 比較は EBCDIC 照合(小文字 < 大文字 < 数字 ― ASCII の逆順)を使うので 'A' *LT 'a' は実機で (→ GE)、ASCII 文字列比較の素朴な「真」ではない。%BIN はフィールドのバイトを big-endian 符号付き整数(*INT2/*INT4)として読む(ホストの little-endian ではない)。どちらも IBM i のセマンティクスに束縛し、上記でバイト単位検証済み。

価値があるのは実機が炙り出したもの ― 合成テストでは絶対に出ない fidelity gap を見つけ、修正した点です:

  • 固定長 *CHAR:IBM i の文字フィールドは宣言長まで空白パディングされるため、20 バイトの &MSG *CAT '!'! がフィールド末尾から溢れて消え OK になる。SlimeCL は *CHAR を可変長文字列扱いしていた(OK!)。修正済 ― 両 target が固定長を再現。
  • *DEC*CHAR 変換:パック10進の合計を文字フィールドに代入すると、フィールド全幅へ右詰め・ゼロ詰めされる(00000000000000000015)。SlimeCL は素の 15 を出していた。修正済
  • SNDPGMMSG MSG()*CHAR を要求し、CALL の char↔packed パラメータ型整合は数値を静かに壊し得る。どちらも初回接続で露出し、文書化済。

正直な適用範囲。 これは代表3本であって全 corpus ではありません。要点は手法:各プログラムを実機 IBM i でコンパイル・実行し、出力を両 target と diff する再現可能な harness です。本数の拡大は継続作業。実機・バイト単位の transpiler 検証を公開している競合を我々は知りません。

今できること

  • 制御フロー:IF/ELSEDOWHILE/DOUNTIL/DOFORGOTO+ラベル(bash にも安全な Rust にも goto が無いため、プログラムカウンタ状態機械で厳密に再現)。
  • プログラム内サブルーチン(SUBR/CALLSUBR)→ 実関数化。CL の変数はプログラム大域なので忠実にモデル化(Rust=モジュール static + 関数、bash=大域変数上の関数)。
  • :優先順位・括弧を正しく解析。(&A *GT 5) *AND (&B *LT 3)、文字列連結 *CAT、BIF %SST / %SCAN / %LEN / %TRIM / %CHAR / %UPPER / %LOWER
  • 型推論:*DEC → i64/f64、*CHAR → String、*LGL → bool。
  • 古い CL の方言:位置パラメータ形式(CHGVAR &X '1'DCL (&X) (*CHAR))、記号比較演算子(= > < ¬=)、部分文字列代入(CHGVAR %SST(&v s l))、予約語の変数名。
  • OS サービスの runtime shim:データエリア(RTV/CHG/CRT/DLT DTAARA)はプロセス内ストアで実行、オブジェクトロック(ALCOBJ/DLCOBJ)・ファイル上書きは認識済。
  • 単一の静的バイナリ(Rust)― 汎用 x86/Linux で動作、IBM i ランタイム不要、外部ライブラリ不要。

まだできないこと (正直に)

  • 次の課題は compile ではなく挙動検証。 46本すべて compile するが、bit-exact は代表4本で検証済(全46本ではまだ)。byte レベルの意味論(EBCDIC 上の %BIN、パック10進の精度)と OS サービス実行(SBMJOB/RCVMSG は compile するが完全実行は未対応、データエリア・ロックは runtime shim)が正直な残課題。
  • 実機 RPG の約18%は compile しない ― BIF 網羅と cursor host 変数の型付け(SQLRPGLE)が未完。
  • 全 corpus の挙動検証 ― 実機 IBM i での bit-exact 検証は代表セットで達成済(3/3、上記参照)。1,700本超の全 corpus への拡大が継続作業。
  • DDS(表示ファイル / 5250)DB2 for i の埋込 SQL 意味論、QUERY/400 は捕捉済みだが完全実行は未対応。

パイプライン

CL1 lexer  →  CL2 Slot IR + 式パーサ  →  CL6 emitter { bash | Rust }
(COBOL/RPG/PL/I/MUMPS… JAVATEL 変換ファミリ共通の段構成)

例 ― サブルーチンを呼ぶ CL DOFOR ループが idiomatic な Rust に:

i = 1;
while i <= 5 {
    subr_addit();   /* CALLSUBR SUBR(ADDIT) */
    i += 1;
}
fn subr_addit() { unsafe { sum = sum + i; } }   // プログラム大域変数

なぜ「できない所」も公開するか

Micro Focus・Blu Age・AWS MMA 等は target 別の compile 合格率自体ほとんど公表しません。私たちは実機 corpus で lex/emit/compile/hang を数値公開し、穴を名指しし、改善の track record も示します(1 session で hang 10→0)。この透明性こそ差別化です ― 結果を信頼せねばならない金融・公共・SI の評価者にとって、「信じてください 100% です」より「正直で、速く改善している」が効きます。

SlimeCL 製品ページ early-access PoC を相談 ← リソース一覧