AS/400(IBM i)モダナイゼーション ― SlimeCL / SlimeRPG でできること・できないこと
正直な、実機 corpus に基づく解説です。以下の数字は実在する公開 IBM i ソース由来で、都合よく選んだ demo ではありません。できない所も公開します ― 透明性こそが moat だからです(競合は multi-target の合格率自体ほとんど公表しません)。
実機ソースで計測(demo ではなく)
RPG(SlimeRPG → Java/runtime)
| 段階 | 結果 | 読み方 |
|---|---|---|
| 字句解析(lex) | 99.4%(1,701 / 1,711) | 固定形式 RPG/400 含め実機 RPG をほぼ全部読める |
| emit(出力生成) | 99.8% | crash せず構造化変換 |
| javac compile(標本) | 約82% | 正直な穴 ― 周辺 RPG(BIF・cursor 項目)が未完 |
| hang | 0 / 1,711 | emitter 無限ループ 10 件を発見・修正(DO-UNTIL / MONITOR / inline データ構造) |
CL(SlimeCL → bash / Rust)
| 段階 | 結果 | 読み方 |
|---|---|---|
| 字句解析(lex) | 100%(46 / 46) | 実機 CL を全部読める |
| emit | 100%(46 / 46)、hang 0 | 固まらない・落ちない |
| bash 構文OK | 100%(46 / 46) | CL のコマンド連携的な性質に bash が最も自然 |
| Rust compile | 100%(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 の出力とバイト単位で比較しました。これまで「未公開」としていたマイルストーンです。達成しました。
実機 IBM i == SlimeCL bash == SlimeCL Rust がバイト単位で一致。
| プログラム | 実機 IBM i | SlimeCL bash | SlimeCL Rust | |
|---|---|---|---|---|
countdown(1..5 の和、*DEC→*CHAR) | 00000000000000000015 | 00000000000000000015 | 00000000000000000015 | PASS |
exprdemo(括弧・*AND・*CAT) | OK | OK | OK | PASS |
sstdemo(%SST 部分文字列) | HELLO | HELLO | HELLO | PASS |
dtaara(データエリア + ALCOBJ/DLCOBJ) | HELLO | HELLO | HELLO | PASS |
ebcdic('A' *LT 'a' ― EBCDIC 照合) | GE | GE | GE | PASS |
binconv(%BIN('//') ― big-endian *INT2) | …24929 | …24929 | …24929 | PASS |
後の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/ELSE、DOWHILE/DOUNTIL/DOFOR、GOTO+ラベル(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% です」より「正直で、速く改善している」が効きます。
