【入門編】ON ZERODIVIDE条件の捕捉と制御 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネスチルドレンな言語をバリバリ書いてきた方にとって、IBMメインフレームの「PL/I(ピーエルアイ)」という名前を聞くだけで、なんだか古めかしい黒い画面と難しい呪文のようなコードを想像して身構えてしまうかもしれませんよね。

「なんだか難しそう……」
「変なエラーで夜中に運用担当から叩き起こされたらどうしよう……」

大丈夫です、安心してください。今日一緒に見ていくのは、PL/Iにおける「ゼロ除算(ゼロによる割り算)」の恐怖からシステムを華麗に守るためのテクニックです。他言語の経験があるあなたなら、「なーんだ、そういうことね」とすぐに腑に落ちるはずですよ。

それでは、レガシー世界の扉をそっと開けてみましょう。

—

1. なぜPL/Iのゼロ除算は怖いのか?(他言語との違い)

JavaやC言語などでプログラムを書いていて、もしうっかり「10 / 0」なんてコードを実行してしまったらどうなるでしょうか?
大抵の場合は `ArithmeticException` のような例外がスローされ、スタックトレースが出力されて安全に(あるいはログを残して)プログラムが異常終了しますよね。COBOLでも、初期設定のままだとサイズエラーなどでアベンド(ABEND)します。

しかし、IBMメインフレームの素のままでこれをやると、システムコード `S0CB` という、レガシーエンジニアが震え上がるようなハードウェア例外の強制ABENDが直撃します。ダンプリストが吐き出され、夜間バッチが盛大に止まる……なんて事態になりかねません。

ここでPL/Iの優しいところ(そして強力なところ)をご紹介します。
PL/Iには、プログラムが異常な状況に陥ったときに、それをキャッチして「おっと、そこはこう処理してね」と身代わりの処理を走らせる仕組みが標準で備わっています。それが今回焦点を当てる ONユニット です。

—

2. 予約語の呪縛がないPL/Iと「ON ZERODIVIDE」

ここで少し、PL/Iのちょっと変わった(そして面白い)言語仕様についてお話しておきます。
JavaやCOBOLには「予約語(Keyword)」がたくさんありますよね。例えば `IF` や `COUNT` や `DATA` なんて言葉は、勝手に変数名として使えません。

しかし、PL/Iには原則として「真の予約語」が存在しません。
どういうことかというと、極端な話、`IF` という名前の変数を作ることすらコンパイラは許してくれます(※文脈で判断するため、推奨はしませんが!)。

そんな柔軟すぎるPL/Iにおいて、例外を捕捉するための構文が `ON 状態名 (ON-unit)` です。
ゼロ除算をキャッチしたい場合は、ズバリ `ON ZERODIVIDE` という構文を使います。

呪文のように見えますが、英語の文章をそのまま読む感覚で捉えてみてください。
「ゼロで割る(ZERODIVIDE)というイベントが起きた(ON)ときには、この処理をしなさい」――ね、怖くないでしょう?

—

3. 実践!ゼロ除算を優しくいなすPL/Iコード

百聞は一見にしかず。実際にゼロ除算が発生しても、システムを強制終了(ABEND)させずに、スマートにエラーメッセージを出して安全に処理を抜けるプログラムの例を見てみましょう。

実務のバッチ改修やマイグレーション調査でもそのまま参考にしていただけるよう、大文字ベースの伝統的なスタイルで記述しています。

  • ゼロ除算(ZERODIVIDE)の捕捉と制御を行うサンプルプログラム


ZERODIV: PROC OPTIONS(MAIN);

DCL WS-TOTAL FIXED DEC(9,2) INIT(100.00); / 分子(トータル金額) /
DCL WS-COUNT FIXED DEC(5,0) INIT(0); / 分母(件数:ここに0が入る想定) /
DCL WS-RESULT FIXED DEC(9,2); / 計算結果 /
DCL WS-MSG CHAR(80); / メッセージ格納用 /

—————————————————————

  • 1. ZERODIVIDE(ゼロ除算)発生時のトラップ(監視)を宣言

—————————————————————
ON ZERODIVIDE BEGIN;
PUT SKIP LIST(‘【警告】ゼロ除算を検知しました。処理を継続します。’);
WS-RESULT = 0; / ゼロで割ってしまった場合のデフォルト値を安全にセット /
GOTO CALC_END; / 異常終了を回避して、安全な脱出口へジャンプ /
END;

PUT SKIP LIST(‘— 割り算処理を開始します —‘);

—————————————————————

  • 2. 実際にゼロ除算が発生する割り算を実行

—————————————————————

  • ここで WS-COUNT が 0 のため、ハードウェア例外ではなく
  • 上で定義した ONユニット(BEGIN〜END)に処理がジャンプします!

—————————————————————
WS-RESULT = WS-TOTAL / WS-COUNT;

PUT SKIP LIST(‘計算結果は: ‘, WS-RESULT);

CALC_END:
—————————————————————

  • 3. 正常・安全なルートでの終了処理

—————————————————————
PUT SKIP LIST(‘— 割り算処理を正常に抜けました —‘);

RETURN;

END ZERODIV;

このコードのポイント

1. `ON ZERODIVIDE BEGIN; … END;`
このブロックが、いわゆる他言語でいう `try-catch` の `catch` 節のような役割を担います。`WS-COUNT` が `0` の状態で割り算が行われた瞬間、プログラムはガタガタッとクラッシュする代わりに、このブロックへ優しくダイブします。
2. `WS-RESULT = 0;` によるフォールバック
ゼロ除算が起きたときに、結果を強制的にゼロ(あるいは特定の異常値)に書き換えることで、後続の集計処理などがゴミデータで汚染されるのを防ぎます。
3. `GOTO CALC_END;` による制御の復帰
PL/IのONユニット内では、処理をどこに返すかをコントロールできます。今回は安全に計算スキップのラベルへ飛ばすことで、システムを落とさずにバッチを完走させています。

—

4. アーキテクトからのアドバイス:レガシーだからこそ「優しさ」を

メインフレームのシステムは、何十年も前に書かれたコードが現役で動き続けていることが珍しくありません。当時のプログラマも、まさか入力データが「0件」になってゼロ除算が起きるなんて、テストケースから漏らしていたかもしれませんよね。

そんなとき、今回紹介した `ON ZERODIVIDE` のような例外制御が適切に組まれているコードに出会うと、当時の先輩エンジニアの「もし何かあっても、システム全体を巻き込んで落とさないでおこう」という、深い優しさと堅牢性へのこだわりを感じずにはいられません。

JavaやCOBOLの経験があるあなたなら、PL/Iのデータ属性(`FIXED DEC` など)や独特の構文も、仕組みさえ分かってしまえば怖くないはずです。
「見慣れない記法に出会ったら、まずは一つずつ要素を分解して対話するように紐解いていく」――このアプローチで、レガシーシステムの迷宮もスイスイ渡り歩けるようになりますよ。

それでは、次回のレガシー探訪もお楽しみに!

タイトルとURLをコピーしました