こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいは従来型のビジネス言語をバリバリ書いてきた方にとって、IBMメインフレームの「PL/I(ピーエルワン)」という名前を聞くだけで、なんだか古めかしくて難解な要塞のように感じてしまうかもしれませんよね。
でも、安心してください。どんなに歴史のある巨大なシステムであっても、使われている基本原則は一つひとつ紐解いていけば、私たちが普段使っているプログラミング言語と本質的には同じです。
今回は、PL/Iの数ある特徴的な仕組みの中でも、ファイル処理(入出力)の要となる「ON ENDFILEユニットの制御フロー」について、一緒に優しく紐解いていきたいと思います。Javaの例外処理やCOBOLの `AT END` 句とは一味違う、PL/Iならではの奥深い世界を覗いてみましょう!
—
1. 他言語からの挑戦者たちが驚く、PL/Iの「例外処理」の哲学
Javaなら `try-catch` ブロック、COBOLなら `READ … AT END …` という構文がファイルの終端(EOF: End of File)を検知するお馴染みのやり方ですよね。
しかし、PL/Iの考え方は少しユニークです。PL/Iには「割り込み(Condition)」という仕組みがあります。ファイルが終わり(ENDFILE)に達したとき、それはプログラムにとって一種の「イベント(割り込み)」として扱われます。
「おいおい、ファイルが終わったぞ!どうする?」とシステムが教えてくれるわけです。これを受け止めるのが、今回主役となる `ON ENDFILE` ユニット です。
予約語を持たない? PL/Iの優しい(けれど油断ならない)世界
ここでPL/Iの面白い(そして初学者が戸惑う)特徴に触れておきましょう。なんと、PL/Iには「厳密な意味での予約語」がほとんどありません。
どういうことかと言うと、`READ` や `FILE` といったキーワードであっても、あなたが変数名として使えてしまうのです(コンパイラが文脈から判断してくれます)。そのため、コードを書くときは変数名とキーワードがごっちゃにならないよう、ちょっとした気配りが必要になりますが、これも慣れてしまえば自由度の高い相棒のように思えてきますよ。
—
2. `ON ENDFILE` と `REVERT` のダンスを覗いてみよう
ファイルからデータを1件ずつ読み込んでいくループ処理を想像してください。ファイルをゴリゴリ読み進めて、ついにデータが尽きた瞬間、何が起きるでしょうか?
COBOLのように「読んだ瞬間に足元で `AT END` が発動する」のではなく、PL/Iでは 「ファイルが終わった状態でさらに読みに行こうとした瞬間」 に、システムが `ENDFILE` というシグナルを投げます。
そのシグナルをキャッチするのが以下のコードのイメージです。
1
/ ========================================================== /
/ ON ENDFILE ユニットの基本動作サンプル /
/ ========================================================== /
TEST: PROC OPTIONS(MAIN);
/ ファイルの宣言(ここではINFILEという論理ファイル) /
DCL SYSIN FILE INPUT;
DCL REC_DATA CHAR(80);
DCL EOF_FLG FIXED BIN(31) INIT(0);
/ ファイル終端に達したときの「お作法」を定義する /
ON ENDFILE(SYSIN) BEGIN;
EOF_FLG = 1; / フラグを立てて、ループを抜ける準備をする /
PUT SKIP LIST(‘>>> ファイルの終端(EOF)を検知しました。 <<<');
END;
/ ファイルを開く /
OPEN FILE(SYSIN);
/ データの読み込みループ /
DO WHILE(EOF_FLG = 0);
/ 1行読み込み /
READ FILE(SYSIN) INTO(REC_DATA);
/ まだファイルが終わっていなければ処理を続ける /
IF EOF_FLG = 0 THEN DO;
PUT SKIP LIST('読込データ: ' || TRIM(REC_DATA));
END;
END;
/ ファイルを閉じる /
CLOSE FILE(SYSIN);
PUT SKIP LIST('--- すべての処理が正常に終了しました ---');
END TEST;
ここで注目してほしいポイント
1. `ON ENDFILE(SYSIN) BEGIN; … END;`
これがイベントハンドラです。「もし `SYSIN` というファイルで End of File が起きたら、この中の処理を実行してね」とあらかじめ予約(設定)しておくわけです。Javaの `catch` ブロックを外側にポツンと置いておく感覚に似ていますね。
2. フラグによる制御抜け
ONユニットの中で直接ループを抜けることも技術的には可能ですが、実務の現場では上記のようにフラグ(`EOF_FLG`)を操作して安全にループを抜ける設計が好まれます。このほうがコードの流れる方向が追いやすく、後から保守する人が泣かずに済みますからね。
—
3. ちょっと待って!「REVERT」ってなんだ?
さて、ここからが今回のハイライト、`REVERT` のお話です。
「え、一度 `ON` で設定したイベントを、わざわざ元に戻す必要なんてあるの?」って思いますよね。
実は、PL/Iの `ON` ユニットは、有効範囲(スコープ)を持っています。もしサブルーチンやネストしたブロックの中で一時的に `ON ENDFILE` の振る舞いを変えた場合、そのブロックを抜けた後も古い設定が残り続けて予期せぬバグを引き起こすことがあります。
そんなとき、お片付けのために使うのが `REVERT` です。
1
/ サブルーチンや特定ブロック内での例 /
SUB_PROCESS: PROC;
DCL MYFILE FILE INPUT;
/ このブロック内だけ、特別なENDFILEの動きをさせたい! /
ON ENDFILE(MYFILE) BEGIN;
PUT SKIP LIST(‘【サブブロック専用】ファイルが切れました!’);
/ 特別なエラー処理など… /
END;
/ — ここでファイル処理を行う — /
/ (省略) /
/ さぁ、このブロックを抜ける前に、ONユニットの設定を元の状態に戻そう! /
REVERT ENDFILE(MYFILE);
END SUB_PROCESS;
なぜ `REVERT` が大切なのか?
メインフレームの巨大なバッチプログラムは何十、何百という内部プロシージャ(サブルーチン)が入り組んで動いています。
「さっきのルーチンで設定した `ON` のせいで、別のルーチンのファイル読み込みまで変な挙動をし始めたぞ……?」というレガシー特有の呪いを防ぐために、「その場しのぎの例外設定は、使い終わったら綺麗に `REVERT`(巻き戻し)して元の世界に戻してあげる」 というのが、歴戦のメインフレームエンジニアの美学なのです。
—
4. 初学者のあなたへ:怖がらなくて大丈夫です
PL/Iの構文や、こうした割り込み制御の仕組みは、一見すると現代の言語にはない独特の匂いがして身構えてしまうかもしれません。
「もしファイルを読み損ねたらどうしよう」
「ONユニットが暴走したらどうしよう」
そう不安になる気持ち、とてもよく分かります。でも、基本はいつだってシンプルです。
1. 「ファイルを読みに行く」
2. 「もし端っこ(ENDFILE)にぶつかったら、ONユニットが呼ばれる」
3. 「呼ばれたらフラグを立てるなりして、上手にループを抜ける」
4. 「用事が済んだら `REVERT` で綺麗にお片付けする」
この流れさえ頭の中にイメージできれば、どんなに古い基幹システムの改修案件が回ってきても、もう怖くありません。一つひとつ、コードという足場を確実を確認しながら進んでいけば、必ず思い通りに動いてくれますよ。
あなたのメインフレーム・ライフが、実り多い素晴らしいものになりますように!それではまた、次回のレガシー探訪でお会いしましょう。
