なぜ正規化が必要か

COBOLソースコードはメインフレーム全盛期の慣習を色濃く残しており、現代のツールでそのまま扱おうとすると様々な困難が生じる。 代表的な問題を挙げると、差分比較ツールが行ラベルの変化に反応してノイズを大量に出力したり、 簡易的な静的解析スクリプトがコメント行を命令と誤認識したりする。 継続行が別行に分断されているためコマンドラインの grep では完全な文字列がヒットしないケースも多い。

こうした問題に対して 正規化(Normalization) を一段挟むことで、後続の解析・移行・差分比較の品質が大幅に向上する。 正規化は「完全なCOBOLパーサーを書く」という重い作業ではなく、 文字列操作と状態機械(ステートマシン)を組み合わせた実用的なアプローチで実現できる。

before / after — 何が変わるか

以下に典型的な変換例を示す。

Before(元ソース)
000100 IDENTIFICATION DIVISION.
000200* このプログラムはサンプルです
000300 PROGRAM-ID. SAMPLE.
000400 PROCEDURE DIVISION.
000500     MOVE 'HELLO WOR-
000600-    LD' TO WS-MSG.
000700     DISPLAY WS-MSG.  STOP RUN.
After(正規化後)
IDENTIFICATION DIVISION.
PROGRAM-ID. SAMPLE.
PROCEDURE DIVISION.
MOVE 'HELLO WORLD' TO WS-MSG.
DISPLAY WS-MSG.
STOP RUN.

変換の内訳は次のとおり。

  • 列1〜6の 行ラベル(シーケンス番号) を除去
  • 列7が *コメント行 を削除
  • 列7が -継続行 を前行に結合(文字列リテラルも正しく連結)
  • 連続スペース を間詰め
  • ピリオドを区切りとして 1行1命令 に分割(DISPLAY WS-MSG. STOP RUN. → 2行に分解)

正規化の対象を整理する

① 行ラベル(シーケンス番号)
列1〜6の数字文字列。旧来のカード穿孔時代の名残。除去するだけでよい。
② コメント行
列7が * または / の行。静的解析・差分比較では不要なため削除する。
③ 識別領域(73列以降)
列73〜80はプログラム名や連番が入る領域。命令とは無関係なので除去する。
④ 継続行
列7が - の行は前行の続き。文字列リテラル継続の場合は引用符の扱いに注意が必要。
⑤ スペースの間詰め
Area A / Area B のインデントや余分なスペースを除去。ただし文字列リテラル内は変更しない。
⑥ 1行1命令化
ピリオドまたは END-xxx スコープ終端を区切りに分割。文字列中・PIC句・数値リテラルのピリオドを誤検出しないようステートマシンで処理する。

⚠️ 完全なCOBOLパーサーではない

本シリーズの実装はすべての方言・拡張に対応する完全パーサーを目指すものではない。 主要な固定形式COBOLを対象とした実用的な正規化ツールとして設計する。 本番適用前には対象プロジェクト固有の特殊記法を必ず確認すること。

シリーズ構成

全8回で段階的に実装を積み上げる。

PART 02
COBOL固定形式レイアウト — 列番号の意味と正規化の難しさ
PART 03
行ラベル除去・コメント行削除・識別領域の除去
PART 04
継続行の結合 — 列7が「-」の行を前行に連結する
PART 05
スペースの正規化 — 間詰めと文字列リテラル保護
PART 06
1行1命令への分割 — ピリオドの罠・END-xxx・ステートマシン
PART 07
完成コード — CobolNormalizerクラスと動作確認

次の章では…

PART 02 では正規化の前提知識として COBOL 固定形式フォーマットの列定義を詳解する。 列番号を知らずに文字列操作を行うと行ラベルを命令と誤認するなど深刻なバグにつながるため、必ず読んでほしい。

→ PART 02 — COBOL固定形式レイアウトへ