現場で生き残るPL/I:ON ENDFILEの制御と「予約語なき世界」の作法
若手エンジニアからよく相談を受ける。「PL/Iって変数名に予約語が使えない制約がないから、何でも変数名にできて自由でいいですね!」と。確かにその通りだ。`IF`という変数を作ることすらコンパイラは許容する。だが、それは「何をやってもいい」という意味ではない。自由度が高いということは、一歩間違えればデバッグ地獄に直面する諸刃の剣なのだ。
今日は、そんなPL/Iの特殊性と、基幹バッチ処理の心臓部である「ファイル終了制御」について、現場の知見を叩き込もうと思う。
1. 予約語なき世界:命名規則の「暗黙の規律」
PL/Iには、他の言語のような強固な予約語(Reserved Words)が存在しない。キーワードは文脈(Context)によって解釈される。例えば、`IF`という名前の変数を作っても、コンパイラはそれが文頭のキーワードなのか、変数名なのかを前後関係から判別する。
しかし、これを逆手に取って`THEN`や`ELSE`を適当に変数名に使うのは、百害あって一利なしだ。保守担当がコードを読んだ際に、「これは条件分岐なのか、それとも変数なのか?」と一瞬でも思考を停止させるコードは、メインフレーム開発における「負債」でしかない。
鉄則:たとえ可能であっても、キーワードに類する名称は変数名に使うな。 これが、数千ステップの複雑なバッチを何十年も延命させてきた先人たちの教えだ。
—
2. ON ENDFILE:ファイル終端のトラップ手法
ストリーム入出力(`GET LIST`, `PUT LIST`等)やレコード入出力において、ファイル終了を検知する`ON ENDFILE`ユニットは、バッチ処理の「最後の砦」だ。
よくある初心者のミスは、`ON`ユニットを複数回定義したり、制御フローを理解せずに複雑な`GOTO`文を多用することだ。`ON`ユニットはスタックされる。誤った制御はメモリリークや意図しないループを引き起こす。
実践的なコーディング例
以下に、VSAMや順次ファイルを読み込む際の、現場で通用する標準的なパターンを示す。
1
/ —————————————————————— /
/ バッチ処理:ファイル読み込み制御の標準テンプレート /
/ —————————————————————— /
PROC_MAIN: PROC OPTIONS(MAIN);
DCL INFILE FILE RECORD INPUT; / 入力ファイル定義 /
DCL EOF_FLAG BIT(1) INIT(‘0’B); / 終了フラグ /
DCL REC_DATA CHAR(80); / レコード格納用バッファ /
/ ENDFILE条件のトラップ:フラグを立てて処理を抜けるのが定石 /
ON ENDFILE(INFILE) BEGIN;
EOF_FLAG = ‘1’B;
END;
OPEN FILE(INFILE);
DO WHILE(^EOF_FLAG);
READ FILE(INFILE) INTO(REC_DATA);
/ EOFがセットされた後の空読みを防ぐガード /
IF EOF_FLAG THEN LEAVE;
/ ここにメインの業務ロジックを記述する /
CALL PROCESS_REC(REC_DATA);
END;
CLOSE FILE(INFILE);
/ 終了処理 /
RETURN;
PROCESS_REC: PROC(P_DATA);
DCL P_DATA CHAR(80) PARM;
/ データの編集・加工ロジックをここに /
PUT SKIP LIST(‘READ REC: ‘ || P_DATA);
END PROCESS_REC;
END PROC_MAIN;
—
3. なぜ「フラグ制御」が安全なのか
上記のコードで、`ON`ユニット内でいきなり`GOTO`を使ってループを抜ける手法もある。しかし、私はあえてフラグ方式を推奨する。理由は以下の2点だ。
1. 制御フローの可視化: `GOTO`はコードの現在地を飛び越え、保守担当者の追跡能力を著しく低下させる。フラグであれば、`DO WHILE`の条件判定を見れば一目瞭然だ。
2. サブルーチン化の容易さ: `ON`ユニット内で`GOTO`を呼ぶと、そのブロックの外側へのジャンプが必要になる。これはサブルーチン構造が複雑化する大規模バッチにおいては、予期せぬリターンエラーやスタック破壊の温床となる。
現場のエンジニアへ送るアドバイス
バッチ改修の際、古いコードの`ON`ユニットがどこで定義されているか分からず、`ON ENDFILE`が連鎖して多重定義されている地獄のコードに出会うことがあるだろう。
もしそのような修正に遭遇したら、まずは`REVERT`文を使って一度条件をリセットする癖をつけてほしい。
1
REVERT ENDFILE(INFILE); / 古いONユニットの残骸を無効化する /
ON ENDFILE(INFILE) BEGIN; … END;
PL/Iは古い言語だが、その堅牢な設計は、書く側の「規律」によって支えられている。予約語がないという自由を「悪用」するのではなく、自ら課した命名規則と制御構造で「制御」する。それこそが、メインフレームアーキテクトとしての矜持だ。
次回の改修作業では、ぜひこの「フラグ制御」と「ガード節」を意識したコードを書いてみてくれ。バグの温床となるような不可解な挙動は、劇的に減るはずだ。
