こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLといった他の言語の経験はあるけれど、「PL/I(ピーエルワン)」を初めて触ることになり、レガシー特有の作法や奇妙なデータ宣言の数々に面食らっていませんか?
「なんだか難解そう……」
「古い仕様で頭がパンクしそう……」
そんな風に不安を感じている方も、どうぞ安心してくださいね。PL/Iは歴史の深い言語ですが、一つひとつの仕様の裏側にある「意図」を知れば、決して怖くありません。むしろ、ハードウェアの性能を極限まで引き出すための、非常に合理的な仕組みに満ちています。
今回は、そんなPL/Iの奥深い世界から、「TRANSLATE関数による文字変換テーブルのメモリ配置」という、ちょっとマニアックだけど実務では超重要なテーマを紐解いていきます。S/370アーキテクチャの心臓部である「TR(Translate)命令」をPL/Iから華麗に操る極意、一緒に見ていきましょう!
—
そもそも「予約語がない」ってどういうこと?
JavaやCOBOL、Pythonなどでは、`if` や `for`、`MOVE` や `DISPLAY` といった言葉は「予約語」として厳格に保護されていますよね。これらを勝手に変数名として使うことはできません。
しかし、PL/Iの面白いところ(そして最初に戸惑うところ)は、「言語としての厳密な予約語を持たない」という設計思想にあるんです。
どういうことかと言うと、例えば `IF` や `READ` といったキーワードであっても、文脈から判断できるため、変数名として使えてしまうのです。
(※もちろん、コードの可読性が地獄のようになるので、実務でそんな名前をつける人はいませんが!笑)
この「文脈依存」の柔軟な構文規則を持つPL/Iにおいて、今回主役となる `TRANSLATE` 関数と、文字変換の心臓部である「256バイトのテーブル」をどう扱うか。ここに、メインフレームならではのメモリ管理の美学があります。
—
トラブル続出?なぜ256バイトのテーブルが必要なのか
オンライン画面から上がってきた小文字のデータを、基幹システムのマスターに合わせてすべて大文字に変換する――。そんなバッチ処理を想像してみてください。
他の言語であれば、組み込み関数に「小文字→大文字変換」のオプションが用意されていることが多いですよね。しかし、レガシーの世界では、文字コード(EBCDIC)の並びがASCIIとは異なります。さらに、自社独自の特殊な文字コード変換を行いたい場合、システム側で「どの文字を・どの文字に置き換えるか」という256バイトの変換マップ(テーブル)を自前で用意してあげる必要があります。
なぜ「256バイト」なのか?
それは、IBMメインフレームの文字表現の基本であるEBCDICが、1バイト(8ビット)= $2^8 = 256$ 通りの表現を持つからです。「0番地から255番地までのすべてのバイトが、変換後にどの値に化けるべきか」を、256個の要素を持つテーブルで完全網羅するわけですね。
この変換テーブル、毎回の処理のたびにメモリ上で生成していたのでは、何百万件も処理する夜間バッチのパフォーマンスがガタ落ちしてしまいます。だからこそ、「静的領域(STATIC)」にガッチリとメモリを確保し、高速にアドレスを解決する必要があるのです。
—
実践!PL/Iによる高速文字変換コードの解説
百聞は一見にしかず。実際に、静的領域に256バイトの変換テーブルを配置し、`TRANSLATE` 関数(ハードウェアのTR命令に直結します)を呼び出すPL/Iのサンプルコードを見てみましょう。
1
/ ================================================================ /
/ プログラム名: CONVTEST /
/ 概要: 256バイトの静的変換テーブルを用いた高速文字変換 /
/ ================================================================ /
CONVTEST: PROC OPTIONS(MAIN);
/ — 1. 変数および変換テーブルの宣言 — /
/ 変換対象のワークエリア(例として50バイト) /
DCL TARGET_DATA CHAR(50) INIT(‘abcdeABCDE12345’);
/
- 256バイトの静的変換テーブル(STATIC属性)
- – STATIC: プログラム実行中、常にメモリ上の同じ場所に常駐させます。
- – INIT: 初期値を埋め込みます。ここでは簡易的に自分自身で初期化。
/
DCL TRANS_TABLE CHAR(256) STATIC
INIT(
/ 0x00 ~ 0x3F (64バイト分) /
(64)X’00’,
/ 0x40 ~ 0x7F (64バイト分 – スペースや特殊文字など) /
(64)X’40’,
/ 0x80 ~ 0xBF (64バイト分) /
(64)X’80’,
/ 0xC0 ~ 0xFF (64バイト分 – 大文字・小文字・数字など) /
/ ※実際にはここで小文字を大文字にマッピングするバイトを定義します /
(64)X’C0′
);
/ — 2. 処理の実行 — /
DISPLAY(‘変換前: ‘ || TARGET_DATA);
/
- TRANSLATE関数の呼び出し
- 第1引数: 変換したいデータ
- 第2引数: 256バイトの変換テーブル
- 内部でIBMマシンの「TR命令(Translate Instruction)」にコンパイルされ、
- CPUのレジスタとメモリ間を一瞬で駆け抜けます。
/
TARGET_DATA = TRANSLATE(TARGET_DATA, TRANS_TABLE);
DISPLAY(‘変換後: ‘ || TARGET_DATA);
RETURN;
END CONVTEST;
コードのここがポイント!
1. `STATIC` 属性の魔力
もしここに `STATIC` を付け忘れて自動変数(AUTOMATIC)にしてしまうと、プログラムがサブルーチンやブロックに入るたびに256バイトのメモリ確保と初期化が走ってしまいます。`STATIC` を指定することで、コンパイル時にロードモジュールの一部としてメモリ配置が確定し、実行時のオーバーヘッドがゼロになります。これぞメインフレームチューニングの醍醐味です!
2. リピートファクター `(64)X’00’`
PL/I独特の書き方ですが、`(64)X’00’` は「16進数の `00` を64回繰り返す」という意味です。256バイトものテーブルを一行ずつ手書きするのは大変なので、この省略記法が非常に役立ちます。
—
先輩からのアドバイス:レガシー移行で躓かないために
他のモダンな言語に慣れていると、メモリを直接意識するようなデータ宣言や、ハードウェア命令(TR命令など)を意識したコード書きに戸惑うかもしれません。
しかし、「なぜこの256バイトが必要なのか」「なぜSTATICにするのか」という理由は、すべて「圧倒的な処理速度を叩き出すため」という極めてシンプルな目的に行き着きます。
基幹システムの改修やマイグレーションの現場では、こうした先人たちの知恵が詰まったコードに出会うことが多々あります。「古いから汚い・難しい」と切り捨てるのではなく、「ハードウェアを限界まで絞り出すための美しい工夫なんだな」と捉えてあげると、PL/Iとの距離が一気に縮まりますよ。
怖がらなくて大丈夫。一つひとつ紐解いていけば、PL/Iほどロジカルで頼もしい言語はありません。あなたのメインフレームライフが実りあるものになるよう、これからも応援しています!
