4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TL; DR

  • >>define ディレクティブでコンパイル時変数を使用
  • COPY 文を関数の代わりに使用
  • 再帰ができないのでチューリング完全ではない (残念!)

はじめに

人々を魅了してやまないコンパイル時計算 (主語デカ)

C++やRust等で多用される技術ですが、本記事では COBOLのコンパイル時計算 について紹介したいと思います。
読み終わるころには、貴方も DATA DIVISION のしがらみから解放されることでしょう。

おことわり

著者はCOBOL超初心者なので、内容に誤りがありましたらコメント等でご指摘いただけますとありがたいです。

そもそもテーマからふざけた記事におことわりも何もありませんが...

実行環境

  • gcobol (GCC15.0.1)

GCOBOLについては以前の記事でも紹介しています。

コンパイル時計算のやり方

コンパイル時計算には、 defineディレクティブを使用します。

define

defineディレクティブを使用すると、コンパイル時に変数を定義することができます。

>>define n 0

override で値を更新(再定義)することも可能です。コンパイル時計算では基本的にこの override を使用します。

>>define n as n + 1 override

COPY文

上記を繰り返すだけでも計算はできますが、処理をもう少しスマートに管理したいです。COPY文を使用することで、コンパイル時にソースコードを別ファイルから挿入できます。

以下は階乗の計算をするプログラムです。
COPY文 copy "fact.cpy". を使用した箇所には、コンパイル時に原文ファイル fact.cpy に記載されたソースコードが挿入されます。

fact.cbl
       identification division.
       program-id. fact.

       procedure division.
       >>define n 0
       >>define fact 1
      * 10!を計算 
      * copy文を使用すると、コンパイル時に該当箇所に原文ファイルのソースコードが挿入される 
       copy "fact.cpy".
       copy "fact.cpy".
       copy "fact.cpy".
       copy "fact.cpy".
       copy "fact.cpy".
       copy "fact.cpy".
       copy "fact.cpy".
       copy "fact.cpy".
       copy "fact.cpy".
       copy "fact.cpy".
       display fact.

       stop run.
fact.cpy
       >>define n as n + 1 override 
       >>define fact as fact * n override

こうすることで、以下のように階乗計算のプログラムが作成されます。

       >>define n 0
       >>define fact 1
      * copy文を展開
       >>define n as n + 1 override 
       >>define fact as fact * n override
      * copy文を展開
       >>define n as n + 1 override 
       >>define fact as fact * n override
      * ...

コンパイル時プログラミングに詳しい方は「COPYでループが作れるのでは?」と思われたかもしれませんが、残念ながら COPYでは自分自身を指定することができないため不可能でした(後述)。

コンパイル時変数を参照

最後に、計算されたコンパイル時変数を(通常のデータ項目のように) display に指定することで内容を表示します。実行されるプログラムでは実質計算結果の数値定数をdisplayしているのと同じです。

fact.cbl
display fact.

表示時は左端に符号が付きます。

実行結果
+3628800

別のデータ項目へmoveすれば任意の書式で出力可能です。今回はdata division禁止縛りなので使いませんが

fact.cbl
      * ...

       data division.
       working-storage section.
       01 fact-printing pic 9(8).

       procedure division.
      * ...
       move fact to fact-printing.
       display fact-printing.

       stop run.
実行結果
03628800

条件分岐

より複雑な処理をするためには条件分岐を使用します。 >>if>>evaluate が使用可能です。
通常の ifevaluate と同じ構文で、先頭に >> を付けるだけでコンパイル時処理になります。

通常のif文と異なるのは、コンパイル時計算なので条件を満たした行だけがプログラムに含まれるという点です

これが
>>define n as 90
>>if n > 60
    display "OK".
>>else
    display "NG".
>>end-if
こうなる
display "OK".

実際にプログラム中にif文があるわけではないため、then節、else節の各文の文末は;ではなく.にする必要があります 1

以下はfizzbuzzの分岐処理を書いた例です。階乗計算同様COPY文を使用し処理を原文ファイルにまとめています。

fizzbuzz.cpy
       >>if n / 15 * 15 = n
           display "FizzBuzz".
       >>else
           >>if n / 3 * 3 = n
               display "Fizz".
           >>else
               >>if n / 5 * 5 = n
                   display "Buzz".
               >> else
                   display n.
               >>end-if
           >>end-if
       >>end-if

       >>define n as n + 1 override

>>evaluate も使用できるはずなのですが、when節が上手くパースできませんでした...)

COPY文で呼び出すとこうなります。

fizzbuzz.cbl
       identification division.
       program-id fizzbuzz.

       procedure division.
       >>define n 1
       copy "fizzbuzz.cpy".
      * ... (計15行)
       copy "fizzbuzz.cpy".

       stop run.
実行結果
+1
+2
Fizz
+4
Buzz
Fizz
+7
+8
Fizz
Buzz
+11
Fizz
+13
+14
FizzBuzz

想定通り出力されました。

制約

ここまで簡単なプログラムをコンパイル時計算で実装してきましたが、この手法にはいくつか制約があります。

COPY文の再帰はできない

先にも述べたように、原文ファイル内のCOPY文で自身を参照することはできません。
別の原文ファイルを経由して間接的に参照する(a.cpyb.cpya.cpy)こともできません。

looper.cpy:6:1: error: recursive copybook: 'looper.cpy' includes itself
    6 |        end-if
      | ^
cobol1: error: failed compiling copy.cbl
  • エラー定義個所

言い換えると、処理を繰り返す場合はその回数分だけ COPY を記載する必要があります。無限ループを作ることはできません
言語の安全機構としては素晴らしいですが、コンパイル時計算はチューリング完全ではなくなってしまいました。COBOLのコンパイル時brainf*ck等は実現困難です2

コンパイル時変数はint32

もう1点の制約として、コンパイル時変数はint32なので、大きな数値を扱うことができません。扱いたい場合は上位nビット、下位nビットのように変数を分ける必要があります。

max.cbl
       identification division.
       program-id define-max.

       procedure division.
       >>define n as 2147483648
       display n.

       stop run.
結果がオーバーフロー
-2147483648

おわりに

以上、COBOLのコンパイル時計算についての紹介でした。皆さんも、究極の省メモリ、高速実行を体感してみてはいかがでしょうか?

  1. ; を間違えて指定してもコンパイルエラーが出てくれるとは限らず、間違った結果が出てしまう可能性もあります(例えば、display n の文末を ; にすると n が無駄に1足された状態になってしまいました)。

  2. そもそも define では文字列操作ができないように見えるので、ソースコードのパースの時点で望み薄です...

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?