こんにちは!メインフレームの荒波へようこそ。
JavaやCOBOLの経験を積んで「さあ、次はPL/Iだ!」と意気込んでいるものの、目の前に広がる独特な大文字のコードや、C言語ともCOBOLとも違う空気に、ちょっぴり圧倒されていませんか?
「変数名に予約語がないってどういうこと?」
「配列の範囲外アクセスって、COBOLみたいに勝手に止まってくれないの?」
そんな不安を抱えているあなたへ。大丈夫ですよ、一つずつ紐解けばPL/Iは決して怖くありません。むしろ、その懐の深さと強力な機能を知れば、きっと好きになります。今回は、PL/Iのちょっと変わった識別子のルールに触れつつ、バグ退治の最強の相棒である`SUBSCRIPTRANGE`(サブスクリプト・レンジ)について、現場の知見を交えてたっぷり解説していきますね。
—
1. 予約語がない!?PL/Iの自由すぎる識別子ルール
JavaやCOBOLを触ってきた人にとって、一番最初に「えっ?」と驚くポイントがこれです。PL/Iには、いわゆる「絶対に変数名として使ってはいけない予約語」というものが原則として存在しません。
例えば、COBOLなら `MOVE` や `DISPLAY` は変数名に使えませんよね。でも、PL/Iの世界では、こんなコードが書けてしまいます。
1
DCL IF FIXED BIN(31); / 「IF」という名前の変数 /
DCL THEN FLOAT; / 「THEN」という名前の変数 /
IF = 10;
「えっ、コンパイラはどうやって `IF` が条件分岐の `IF` なのか、変数なのかを判断しているの?」と思いますよね。
実はPL/Iのコンパイラは、その前後の文脈(コンテキスト)をものすごく賢く読み取っています。人間が文章を読むとき、「『りんご』を食べた」の「りんご」が名詞だと自然に理解するのと同じです。
ただ、自由度が高いからといって、わざわざ `IF` や `DO` を変数名にするのは、後からコードを読むメンテナンス担当者(あるいは未来のあなた)を絶望させるだけなので絶対にやめましょうね(笑)。
—
2. 配列の「うっかり」を救う SUBSCRIPTRANGE
さて、本題に入りましょう。基幹システムのバッチプログラムを改修していて、最も恐ろしいバグの一つが「配列の添字(サブスクリプト)溢れ」です。
例えば、10個しか要素がない配列 `DCL WORK_TBL(10) FIXED BIN(31);` なのに、ループのカウンタが狂って `11番目` にアクセスしてしまったとします。
C言語なんかだと、運が悪いとそのままメモリの違う領域を破壊し、全然関係ない場所で謎の異常終了(セグメンテーション違反)を起こしたりしますよね。
PL/Iには、こうしたメモリ破壊を防ぎ、配列の境界チェックを行ってくれる神機能があります。それが `SUBSCRIPTRANGE` 条件です。
デフォルトでは無効になっている理由
「え、じゃあ最初から有効にしといてよ!」と思いますよね。
しかし、メインフレームの世界では「実行時パフォーマンス(CPU時間)」が神様です。すべての配列アクセスに対して「今、範囲内か?」とチェックを行っていると、何百万件もループを回す巨大なバッチ処理では、目に見えて処理速度が落ちてしまいます。
そのため、PL/Iではデフォルトではこのチェックがオフになっています。本番環境のスピードを優先するためですね。
—
3. デバッグの現場で SUBSCRIPTRANGE を有効にする方法
では、テスト環境で「なんだか変なデータが取れるぞ」「原因不明のABEND(異常終了)が起きるぞ」という怪奇現象に遭遇したときはどうすればいいでしょうか?
答えは簡単、プログラムの中でこの監視を「オン」にしてやればいいのです。
実際のコード例を見てみましょう。
1
/ ———————————————— /
/ SUBSCRIPTRANGE デバッグ活用のサンプルプログラム /
/ ———————————————— /
TEST_PROG: PROC OPTIONS(MAIN);
/ コンパイル時および実行時に範囲チェックを有効化する宣言 /
(SUBSCRIPTRANGE): プレフィックス(接頭辞)で範囲チェックを指定します /
(SUBSCRIPTRANGE) BEGIN;
DCL ITEM_CNT FIXED BIN(31) INIT(5);
DCL PRICE_LIST(10) FIXED BIN(31); / 要素数10の配列 /
DCL I FIXED BIN(31);
/ わざと10を超える範囲にアクセスするバグを仕込んでみます /
DO I = 1 TO 12;
PRICE_LIST(I) = I 100; / I=11 や I=12 の時に溢れます /
END;
END;
END TEST_PROG;
このコードを実行すると、`I = 11` になった瞬間にコンパイラが提供するONユニット(例外処理機構)が働き、「IBMZ などの環境で `IBM0221S SUBSCRIPTRANGE condition was raised`」といった分かりやすいエラーメッセージを出して、プログラムを安全に止めてくれます。
どこで配列がはみ出たのか一発で特定できるため、デバッグの効率が劇的に跳ね上がります。
—
4. アーキテクトからの実務アドバイス
ここで、現場のシニアエンジニアからの大切なアドバイスを一つ。
「便利だからといって、本番稼働するプログラムに `(SUBSCRIPTRANGE)` をつけっぱなしにするのは絶対にやめましょう。」
本番環境の膨大なトランザクション処理において、すべての配列アクセスに監視コストを支払うのは、システム全体のスループット低下(CPU使用率の高騰)を招きます。
おすすめの運用フローはこうです:
1. 開発・単体テスト・結合テスト環境: コンパイルオプション(またはプログラム内のプレフィックス)で `SUBSCRIPTRANGE` を常に有効にしておく。
2. 本番環境: テストを通過し、品質が担保されたコードであることを前提に、このチェックは外して(無効化して)ビルド・リリースする。
PL/Iは、私たちがしっかりと手綱を握ってあげれば、これほど頼もしく、かつ高速にデータを処理してくれる言語はありません。ルールや仕様で分からないところに出会ったら、「昔のプログラマは、どうやってマシンを効率よく動かそうとしたんだろう?」と背景に思いを馳せてみてくださいね。
それでは、快適なメインフレーム・ライフを!次回の解説もお楽しみに。
