こんにちは!メインフレームの世界へようこそ。システムアーキテクトの私です。
JavaやCOBOLといったモダン、あるいは従来の基幹系言語をバリバリ書いてきた方にとって、IBMメインフレームの「PL/I(ピーエルワン)」という名前を聞くだけで、なんだか古めかしい難解な呪文のような印象を受けてしまうかもしれませんよね。「変数はどう宣言するの?」「ファイル終了ってどうやって検知するの?」と、最初は戸惑うことばかりだと思います。
でも、安心してください。ベースにある考え方を一つずつ紐解いていけば、PL/Iは非常に表現力豊かで、実はエンジニアに優しい素晴らしい言語なんです。
今回は、PL/Iの数ある特徴の中でも、特に初学者が「おや?」とつまずきやすい「識別子の命名規則と予約語の概念(の薄さ)」、そして実務のバッチ処理で避けて通れない「ON ENDFILEユニットによるファイル終端処理の制御構造」について、コーヒーでも飲みながらリラックスした気持ちでマスターしていきましょう。
—
1. JavaやCOBOLとはちょっと違う?PL/Iの「名前」と「予約語」の秘密
JavaやC言語では、`if` や `while`、`class` などの単語を「予約語(リザーブド・ワード)」と呼び、変数名として使うことは絶対にできませんよね。「総売上」を表す変数に `class` なんて名付けようものなら、コンパイラから怒られてしまいます。
しかし、PL/Iの世界は少し(というか、かなり)自由奔放です。
予約語がない!?コンテキスト依存の世界
実は、PL/Iには厳密な意味での「予約語」が存在しません。
どういうことかと言うと、極端な話、プログラムの制御構文であるはずの `IF` や `READ` さえも、変数名として使うことができてしまうのです。
「えっ、それじゃあコンパイラはどうやってそれが『命令』なのか『変数』なのかを判断しているの?」と不安になりますよね。
コンパイラは、前後の文脈(コンテキスト)を見て、「あ、ここは位置的に命令だな」「ここは変数名だな」と賢く判断しています。
とはいえ、実務の現場で `IF` という名前の変数を作る人はまずいません(後輩から冷たい目で見られます)。ただ、COBOLの膨大な予約語リストに頭を悩ませた経験がある方にとって、「キーワードに縛られすぎない」というPL/Iの懐の深さは、慣れるとなかなか心地よいものですよ。
—
2. ファイル終端(EOF)の恐怖?を優しく包み込む「ON ENDFILE」
さて、ここからが本題です。ファイルを順番に読み込んでいくバッチ処理において、避けて通れないのが「ファイルの終わり(End of File)」をどう検知するかという問題です。
他の言語、例えばCOBOLであれば `READ FILE AT END …` と書いたり、Javaなら `while ((line = reader.readLine()) != null)` とループの条件式で判定するのが一般的ですよね。
しかし、PL/Iのファイル読み込み制御は、少しユニークでドラマチックです。
PL/Iでは、ファイルの読み込み自体は非常にシンプルな構文で行い、「もしファイルの終端に達したら、自動的にこの割り込み処理(イベント)を発生させてね」という網(トラップ)を事前に張っておくスタイルをとります。
これが `ON ENDFILE` ユニット です。
実例で見てみましょう:PL/Iのファイル読み込みプログラム
百聞は一見にしかず。実際のPL/Iコードを見てみましょう。大文字で書かれているのがレガシーっぽくてカッコいいですよね。もちろん、実務でそのまま参考にしていただけるよう、丁寧な日本語コメントを添えています。
1
/ ======================================================== /
/ 顧客マスタファイルを順次読み込み、終端まで処理するサンプル /
/ ======================================================== /
CUST_PROCESS: PROC OPTIONS(MAIN);
/ 1. ファイル(データセット)の論理的な定義 /
DCL CUST_FILE FILE RECORD INPUT;
/ 2. 読み込むレコードの構造体(レイアウト)定義 /
DCL 1 CUST_REC,
3 CUST_ID CHAR(5), / 顧客ID /
3 CUST_NAME CHAR(30); / 顧客名 /
Dcl EOF_FLG CHAR(1) INIT(‘OFF’); / 独自で用意する終了フラグ /
/ 3. ファイルを開く /
OPEN FILE(CUST_FILE);
/ ======================================================== /
/ 4. 【ここが今回のメイン!】ENDFILE割り込みの定義 /
/ CUST_FILEの読み込み中にファイルの終端に達した瞬間、 /
/ BEGINブロック内の処理が「割り込み」として実行されます。 /
/ ======================================================== /
ON ENDFILE(CUST_FILE) BEGIN;
EOF_FLG = ‘ON’; / 終了フラグをONにする /
PUT SKIP LIST(‘【INFO】顧客ファイルの終端(EOF)を検知しました。’);
END;
/ 5. 最初のレコードを読み込む(プリ・リード) /
READ FILE(CUST_FILE) INTO(CUST_REC);
/ 6. 終端フラグがONになるまでループを回す /
DO WHILE (EOF_FLG = ‘OFF’);
/ 読み込んだデータの加工・出力処理 /
PUT SKIP LIST(‘顧客ID: ‘ || CUST_ID || ‘ / 顧客名: ‘ || CUST_NAME);
/ 次のレコードを読み込む /
READ FILE(CUST_FILE) INTO(CUST_REC);
END;
/ 7. ファイルを閉じる /
CLOSE FILE(CUST_FILE);
PUT SKIP LIST(‘すべての処理が正常に終了しました。’);
END CUST_PROCESS;
コードの解説:何が起きているの?
1. `ON ENDFILE(CUST_FILE) BEGIN; … END;`
これがイベント駆動型のファイル制御です。プログラムの実行中に `CUST_FILE` に対して `READ` を実行し、もう読み込むデータがなくなった瞬間、CPUがハッとしてこの `ON` ユニットにジャンプします。
2. 割り込み先での制御
ここではシンプルに `EOF_FLG = ‘ON’;` とフラグを立てていますが、この中で後処理のルーチンを呼び出したりすることも可能です。
3. イベントの連動(シミュレーションの観点)
実務のテスト工程や単体テストにおいて、「データが空っぽのファイル(空ファイル)」や「途中で異常終了するケース」をシミュレートしたい場合、わざわざテストデータを作り直さなくても、プログラム内で `SIGNAL ENDFILE(CUST_FILE);` という文を実行することで、「あたかもファイルの終端に達したかのようなイベントを人工的に発生させる」ことができます。これがPL/Iの非常に強力で面白いところです。
—
3. まとめ:レガシーの世界は怖くない
いかがでしたでしょうか?
「ファイルが終わったらどうしよう」という不安を、`ON ENDFILE` という専用の番人に任せてあげることで、メインのループ処理(`DO WHILE`)を非常にシンプルに保つことができます。
JavaやCOBOLの書き方に慣れていると、最初は「イベントドリブンでファイルを制御する」という感覚に少し戸惑うかもしれません。しかし、エラー処理や例外的なシチュエーション(ファイル終端もその一種です)をメインストリームのロジックから綺麗に分離できるこの仕組みは、大規模な基幹システムを長年支えてきた先人たちの知恵の結晶です。
「識別子に予約語がない」「ファイル終了はイベントで捉える」。
そんなPL/Iのユニークなルールも、一つひとつ紐解いていけば怖くありませんよね。
日々のマイグレーション調査やバッチ改修の現場で、この記事があなたの心強い羅針盤となれば幸いです。それでは、また次回のメインフレーム談義でお会いしましょう!
