こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいは従来型のビジネス言語をバリバリ書いてきた方にとって、IBMメインフレームの「PL/I(ピーエルワン)」という名前は、少し古めかしく、どこか近寄りがたいダンジョンのように感じられるかもしれませんね。
「なんだか変数名のルールも独特だし、ポインタはあるし、エラーが出たら怖そう……」
そんな風に身構えていませんか? 大丈夫です、安心してください。
今日は、PL/Iプログラムが動き出す最初の瞬間――`OPTIONS(MAIN)` の初期化処理とランタイム環境の裏側について、JavaやCOBOLの常識と対比しながら、優しく、かつ深く紐解いていきますよ。
—
1. 他言語からの旅人へ:PL/Iの「名前」と「予約語」の優しい世界
まず、JavaやCOBOLから来た人が最初に驚くのが、PL/Iの「識別子(変数名)のルール」と「予約語(キーワード)の懐の深さ」です。
JavaやC言語では、`if` や `while`、`class` などの予約語は絶対に変数名に使えませんよね。「あ、うっかり変数名に `total` ってつけたら予約語だった!」なんてコンパイルエラーに悩んだ経験もあるでしょう。
しかし、PL/Iは違います。PL/Iには、いわゆる「狭義の予約語(使ってはいけない単語)」がほとんど存在しないのです。
例え話:文脈を読んでくれる優秀な執事
PL/Iのコンパイラは、まるで超優秀な執事のようなものです。
例えば、コードの中に `IF = 3;` と書かれていたとします。Javaなら「は? `if` は条件分岐のキーワードでしょ!何を代入してんの!」と怒り狂うところです。
でもPL/Iの執事は、こう考えます。
「ほう、まわりの文脈を見るに、この `IF` はどうやら変数として使いたいんだな。よし、文脈から判断して変数として扱ってやろう」
このように、PL/Iではキーワードと同じ単語を自由に変数の名前として使うことができます。初学者泣かせの仕様に見えますが、慣れると「なんて寛大な言語なんだ……」と感動すら覚えるポイントです。
—
2. すべての始まり:`OPTIONS(MAIN)` が呼ばれるとき
さて、Javaには `public static void main(String[] args)` があり、COBOLには `PROCEDURE DIVISION.` がありますね。
PL/Iでプログラムの玄関口(エントリーポイント)を指定するのが、お馴染みの `OPTIONS(MAIN)` です。
では、この `OPTIONS(MAIN)` が宣言されたプロシージャ(サブルーチンや関数のようなもの)が実行されるとき、裏側ではいったい何が起きているのでしょうか?
ランタイム環境(LE: Language Environment)の登場
メインフレーム上でPL/Iプログラムがロードされると、OSが直接コードを叩くわけではありません。その下支えをしているのが IBM Language Environment (LE) というランタイム環境です。
`OPTIONS(MAIN)` は、LEに対してこう叫んでいます。
「さあ、ここが我がプログラムの王様(メイン)の部屋だ!王を迎えるための豪華なベッドルーム(ワークエリア)の用意と、お供(引数)の準備をしなさい!」
この初期化プロセスでは、以下のステップが水面下で一瞬にして行われます。
1. ストレージの獲得(ダイナミック・ストレージ)
プログラム内で使う変数や作業領域(スタック)のためのメモリ領域がガバッと確保されます。
2. JCL(Job Control Language)からの引数の受け渡し
メインフレームのジョブ実行時に `PARM=’XYZ’` などと指定した文字列が、PL/Iのメインプロシージャの引数へと安全に引き渡されます。
3. エラーハンドリング(条件処理)のフック設定
もし途中でゼロ除算やファイルが見つからないエラーが起きたときのために、LEの救急救命士(異常終了ハンドラ)がスタンバイします。
—
3. 実践! `OPTIONS(MAIN)` を使った実用的なプログラム例
百聞は一見にしかず。実際に `OPTIONS(MAIN)` を持った王道かつ実用的なPL/Iのコードを見てみましょう。
JCLから渡されたパラメータを受け取り、初期化処理を経てコンソールにメッセージを吐き出すサンプルです。
1
HELLO: PROC(P_PARM) OPTIONS(MAIN);
/ ======================================================== /
/ プログラム名:HELLO /
/ 概要:OPTIONS(MAIN)による初期化とパラメータ受取のサンプル /
/ ======================================================== /
/ 1. 引数の定義 /
/ CHARACTER VARYING はJavaのStringやCOBOLのX(n) みたいなもの /
/ 外部から渡される文字列の長さを動的に受け取ります /
DCL P_PARM CHAR(100) VARYING;
/ 2. 内部変数の定義 /
DCL W_MSG CHAR(120) VARYING;
DCL W_CNT FIXED BIN(31) INIT(0); / 32ビット整数カウンタ /
/ — 初期化処理スタート(LEが環境を整えた直後) — /
W_CNT = W_CNT + 1;
/ パラメータが指定されているかチェック /
IF P_PARM ^= ” THEN DO;
W_MSG = ‘ようこそ、メインフレームへ! パラメータ: ‘ || P_PARM;
END;
ELSE DO;
W_MSG = ‘パラメータはありませんが、今日も元気に稼働中!’;
END;
/ メッセージをシステムログ(SYSOUT)に出力 /
PUT SKIP EDIT (W_MSG) (A);
/ 正常終了(LEへ制御を戻す) /
RETURN;
END HELLO;
コードのここがポイント!
- `PROC(P_PARM) OPTIONS(MAIN)`: ここがメインの入り口です。`P_PARM` にはJCLの `PARM=` で指定した値が自動的にセットされます。
- `DCL (DECLARE)`: 変数宣言です。COBOLの `DATA DIVISION` やJavaの型宣言にあたります。`FIXED BIN(31)` はCOBOLの `COMP`(バイナリ整数)と同じだと思ってください。
- `||`(結合演算子): 文字列と文字列をつなぐ演算子です。Javaの `+` やCOBOLの `STRING` 命令のスマート版ですね。
—
4. 初学者がハマりやすい「初期化」の罠とアドバイス
最後に、レガシー移行やバッチ改修でよくある「初期化にまつわるトラブル」を一つ。
Javaや最近の言語では、変数を宣言したときに自動で初期値(数値なら `0`、オブジェクトなら `null`)が入ることが多いですよね。
しかし、PL/Iのデフォルト(特に自動ストレージ変数)は、「初期化を明示しないと、ゴミ値(ガベージ)が入ったまま」になることがあります。
ですので、今回のテーマである「初期化処理」を確実に行うために、変数宣言の際は可能な限り `INIT` 句を使う、あるいはプロシージャの冒頭でしっかりと初期化する癖をつけておくのが、ベテランメインフレームアーキテクトからの心からのアドバイスです。
1
/ 安全な初期化の例 /
Dcl W_Total_Amount Fixed Bin(31) Init(0);
「なんだかルールが多くて難しそう」に見えたPL/Iも、こうして一つずつ紐解いていくと、プログラマの意図に忠実に、かつパワフルに動いてくれる素直な言語であることが分かっていただけたのではないでしょうか。
メインフレームの巨大なシステムも、突き詰めればこうした基本の積み重ねです。怖がらなくて大丈夫、あなたのその手で、レガシーな世界に新しい風を吹き込んでいきましょう!
