こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語や基幹言語をバリバリ書いてきた方にとって、IBMメインフレームの「PL/I(ピーエルアイ)」という名前を聞くだけで、なんだか古めかしくて難解な要塞のように感じてしまうかもしれませんよね。
「変数の命名規則はどうなっているの?」
「データ型がなんだか独特で怖い……」
そんな不安を抱えていらっしゃるあなたへ。大丈夫です、安心してください。一見すると厳めしいPL/Iの仕様も、本質を掴んでしまえば、かゆいところに手が届く非常に合理的でエレガントな言語なんですよ。
今回は、数あるPL/Iの機能の中から、現場のバッチ改修やデータ移行(マイグレーション)でめちゃくちゃお世話になる「BIT関数による数値からビット列への変換」をテーマに、内部の仕組みをこっそり覗いてみましょう。
—
そもそもPL/Iの変数名って縛りが緩いの?(予約語の話)
JavaやCOBOLでは、「この単語はシステムで予約されているから変数名に使っちゃダメ!」という予約語(Reserved Words)の壁に何度も阻まれた経験はありませんか?
実は、PL/Iの最もユニークで、初学者が最初に「えっ?」と驚くポイントがここにあります。PL/Iには、厳密な意味での「予約語」というものが存在しません。
どういうことかと言うと、`IF` や `THEN`、さらにはデータ型の定義で使う `FIXED` や `BINARY` といったキーワードでさえも、コンテキスト(文脈)によって判断されるため、極端な話、変数名として使えてしまうのです。
1
/ こんなお茶目な宣言も、PL/Iのコンパイラは怒りません(※実務ではやめましょうね!) /
DCL FIXED FIXED(31,0) BIN;
「え、じゃあコンパイラはどうやって区別しているの?」と思いますよね。
PL/Iは、その名の通り「プログラミング言語ワン」として、あらゆる用途を1つに統合することを目指して作られたため、「置かれている位置(文脈)」から意味を推論する能力がもの凄く高いのです。
とはいえ、フリーダムすぎる命名は後任のプログラマー(あるいは未来のあなた)を絶望のどん底に突き落としますので、「意味のある分かりやすい名前をつける」というJavaやCOBOLでの美しい習慣をそのままPL/Iでも活かしていきましょうね。
—
今回の主役:FIXED BINARY と BIT関数の深イイ関係
さて、本題に入りましょう。基幹システムのデータ移行や電文解析を行っていると、こんなシチュエーションに必ず直面します。
> 「オンラインから送られてきたパック十進数(DECIMAL)や内部整数(FIXED BINARY)のフラグ項目のビット状態を、個別に判定・加工したい!」
他の言語なら、ごちゃごちゃとビットシフト演算子(`<<` や `>>`)を書いてマスク処理をするところですが、PL/Iには「BIT関数」という、魔法のような組み込み関数が用意されています。
まずは、コードの全体像を見てみましょう。
1
/ ========================================================== /
/ FIXED BINARY 値をビット列に変換するサンプルプログラム /
/ ========================================================== /
TEST_CONV: PROC OPTIONS(MAIN);
/ 変数の宣言 /
DCL W_DEC_VAL FIXED BIN(31) INIT(13); / 変換元の数値(10進数の 13) /
DCL W_BIT_STR BIT(32); / 変換先の32ビット列 /
DCL W_SUB_BIT BIT(4); / 下位4ビットを切り出す用 /
/ 1. 数値をそのまま32ビットのビットパターンに変換する /
W_BIT_STR = BIT(W_DEC_VAL);
/ 2. 結果を出力してみる(実際はSYSOUTやログファイルに出力) /
PUT SKIP EDIT (‘数値: ‘, W_DEC_VAL, ‘ -> ビット: ‘, W_BIT_STR)
(A, F(4), A, A);
/ 3. 特定の範囲だけを切り出す(スライス操作) /
/ 13 の2進数は …0000 1101 なので、下位4ビットは ‘1101’B になるはず /
W_SUB_BIT = W_BIT_STR(29:32);
PUT SKIP EDIT (‘下位4ビット: ‘, W_SUB_BIT) (A, A);
END TEST_CONV;
このコード、パッと見はシンプルですが、IBMメインフレームの内部では何が起きているのでしょうか?
—
内部のビットマスクとストレージの仕組みを紐解く
メインフレームの心臓部において、`FIXED BIN(31)` は、4バイト(32ビット)の領域をガッチリと専有する整数データとして保管されています。
数値の `13` を2進数で表すと、符号ビット(一番左)を含めて、下位の桁に `1101` が並んだ状態になります。
[ 4バイト (32ビット) のストレージイメージ ]
00000000 00000000 00000000 00001101 (合計32ビット)
ここで `BIT(W_DEC_VAL)` を実行すると、PL/Iコンパイラは次のようなウルトラCを裏でやってのけます。
1. データ型の強制解釈(Type Casting)の免罪符
Javaなどの厳格な型システムであれば、「数値からビット列への無謀なキャストです!」とコンパイルエラーになるところを、PL/Iは「おっ、この4バイトのメモリ領域を、そのまま『文字ならぬビットの並び』として扱いたいんだね」と、メモリの物理構造をそのままスライドさせて `BIT(32)` 型の変数へ流し込んでくれます。
2. 高速なビットマスクとシフトの自動展開
手動でシフト演算子を書かなくても、PL/Iのランタイム(あるいは生成されるマシン語コード)は、ハードウェアレベルの命令(S390/zアーキテクチャのビット操作命令など)を駆使して、一瞬でこの変換を完了させます。
さらに、PL/Iが素晴らしいのは、コード内にもあった `W_BIT_STR(29:32)` のような「部分文字列参照(Substring Notation)」がビット列に対しても直感的に使える点です。
「配列の何番目から何番目まで」を指定する感覚で、フラグのオン・オフをピンポイントでバシッと切り出せるのは、レガシーバッチの電文レイアウト解析において本当に強力な武器になります。
—
まとめ:PL/Iは怖くない!
いかがでしたでしょうか?
「識別子の規則が自由すぎる」「データ型の宣言が独特で呪文のよう」と言われるPL/Iですが、その裏側にあるのは、「プログラマの意図を最大限に汲み取り、メモリやハードウェアの効率を極限まで高めて実行する」という、メインフレーム時代からの熱い思想です。
今回ご紹介した `BIT` 関数と `FIXED BIN` の組み合わせさえマスターしてしまえば、複雑な電文のビットフラグ判定も怖くありません。
「変な記法を見かけたら、まずはメモリの物理構造をイメージしてみる」
これさえ意識すれば、あなたも立派なPL/I使いの仲間入りです。
レガシーシステムの改修や移行作業、大変なこともあるかとは思いますが、一つひとつ紐解いていけば必ず理解できます。ぜひ、日々の開発にこの知見を役立ててくださいね!
