1人COBOL再研修〜基本構文編〜

[これ](https://qiita.com/zama_8722/items/65935680e157de2c7927)の続き。今回は基本文法編ということで、前回も使った文法も含めて総ざらい。

※ 横ルーラの記載方法が変わっていますが、見方は前回と同様です。プログラムにコメントとして埋め込みたかったので変えています。

## 画面出力・入力
### 画面出力

```:記法
DISPLAY   <出力したいもの>.
```

任意の文字やデータ項目を出力することができる。Cの`printf()`のようなもの。文字リテラルと変数を一緒に出したいときは`+`をつけて文字列連結したくなると思うが、COBOLでは何もつけずに後ろに一緒に出したいデータ項目や文字リテラルを書く。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  STR                PIC X(5)
                                VALUE 'WORLD'.
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
       MAIN-S.
         DISPLAY              "HELLO " STR.
       MAIN-E.
         STOP RUN.
```

実行結果

```
HELLO WORLD
```

### 入力

```:記法
ACCEPT   <入力先>  [FROM  <入力ソース>].
```

画面入力を行う。Cでいう`scanf()`のようなもの。入力先には入力データの格納先のデータ項目を記載する。入力ソースはいくつかある。以下は自分が使ったことがあるもの。

|記載方法|入力ソース|
|:---:|:---|
|`CONSOLE`|コンソール|
|`DATE`|YYMMDD形式のシステム日付|
|`TIME`|HHMMSSTT形式のシステム時刻|

小言を言うとこの勉強でHHMMSSの次はTTだって知った。

ちなみに、`FROM   <入力ソース>`を記載しないとコンソールからデータを取ってくる。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  IN-CONSOLE         PIC X(05).
       01  IN-NOTLISTED       PIC X(05).
       01  IN-DATE            PIC 9(06).
       01  IN-TIME            PIC 9(08).
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
       MAIN-S.
      *--ｺﾝｿｰﾙﾆｭｳﾘｮｸ
         ACCEPT               IN-CONSOLE  FROM  CONSOLE.
         ACCEPT               IN-NOTLISTED.
      *--ｼｽﾃﾑｼﾞｺｸ
         ACCEPT               IN-DATE     FROM  DATE.
      *--ｼｽﾃﾑｼﾞｺｸ
         ACCEPT               IN-TIME     FROM  TIME.

      *--ｼｭﾂﾘｮｸ
         DISPLAY              IN-CONSOLE.
         DISPLAY              IN-NOTLISTED.
         DISPLAY              IN-DATE.
         DISPLAY              IN-TIME.
       MAIN-E.
         STOP RUN.
```

実行結果（最初2行は自分で入力したものなので出力しているのは3行目から）

```
HELLO
WORLD
HELLO
WORLD
190615
14291199
```

システム日付やシステム時刻はシステムから勝手に取ってきてくれるので自分で何か入力とかは不要。

## データ操作
### リテラルやデータ項目を他のデータ項目に代入

```:記法
MOVE   <リテラルor元データ項目>  TO  <格納先データ項目>.
```

他の言語でいう`格納先 = 格納元データ`と同じ。COBOLではこれを`MOVE`文でやる。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  IN-STR1            PIC X(05).
       01  IN-STR2            PIC X(05).
       01  CNST               PIC X(05)
                                VALUE 'WORLD'.
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
       MAIN-S.
      *--ﾘﾃﾗﾙｶﾗ
         MOVE                 'HELLO' TO IN-STR1.
      *--ﾃﾞｰﾀｺｳﾓｸｶﾗ
         MOVE                 CNST    TO IN-STR2.
      *--ｼｭﾂﾘｮｸ
         DISPLAY              IN-STR1.
         DISPLAY              IN-STR2.
       MAIN-E.
         STOP RUN.
```

実行結果

```
HELLO
WORLD
```

となると、`1+1`の結果(2)を入れるなら

```
MOVE  1 + 1  TO  RESULT.
```

なのか？と思うかもしれないが、そんなことをするともれなく怒られる（コンパイルエラー）。どうするかは下へ。

### 四則演算を行う

```:記法
COMPUTE  <計算結果格納先> = <計算式>.
```

ただただ四則計算して結果を入れる。それだけ。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  RESULT             PIC 9(02).
       01  IN-NUM             PIC 9(01)
                                VALUE 9.
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
       MAIN-S.
      *--ｹｲｻﾝ
         COMPUTE              RESULT = IN-NUM + 1.
      *--ｼｭﾂﾘｮｸ
         DISPLAY              RESULT.
       MAIN-E.
         STOP RUN.
```

実行結果

```
10
```

## 制御
### 条件分岐

```:記法
IF  <条件式>  THEN
  <TRUE>のときの制御
ELSE
  <FALSE>のときの制御
END-IF.
```

これは他の言語とも似てるからとっつきやすい。普通に条件によって処理を分岐させているだけ。ただ注意したいのは、等価演算子の使い方がちょっと違う。

|よく使われるやつ|COBOLだとこうなる|意味|
|:---:|:---:|:---|
|`A == B`|`A = B`|`A`と`B`が同じ|
|`A != B`|`A NOT= B`|`A`と`B`が違う|

ほかは同じ。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  CNST               PIC 9(02)
                                VALUE 1.
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
       MAIN-S.
         IF     CNST = 1      THEN
           DISPLAY            'TRUE'
         ELSE
           DISPLAY            'FALSE'
         END-IF.
       MAIN-E.
         STOP RUN.
```

実行結果

```
TRUE
```

気をつけなければならないのは`.`（ピリオド）。`IF〜END-IF`が1つの命令になるので、`IF`の中の制御にピリオドを付けてはいけない。これになれるまで苦労したし数週間たった今でもつけてしまう時がある。

ついでにいうと、`IF`が入れ子になったときも同じで、

```
      *---1---------2---------3---------4---------5
         IF     <条件式1>      THEN
           IF   <条件式2>      THEN
             <条件式1・2がTRUE>
           END-IF
         ELSE
           <条件1がFALSE>
         END-IF.
```

こうなる。

### ケース分けがある条件分岐
```:記法
EVALUATE  <検査対象>
  WHEN    <定数1>
    <検査対象が定数1のときの制御>
  WHEN    <定数2>
    <検査対象が定数2のときの制御>
  WHEN    OTHER
    <検査対象が上のどれでもないときの制御>
END-EVALUATE.
```

他言語でいう`switch-case`文になる。結果が複数通り考えられていて、`IF`で書くと複雑になるときにこっちを使えばいい。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  CNST               PIC 9(01)
                                VALUE 1.
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
       MAIN-S.
         EVALUATE  CNST
           WHEN    1
             DISPLAY          'HOGE'
           WHEN    2
             DISPLAY          'FOO'
           WHEN    OTHER
             DISPLAY          'BAR'
         END-EVALUATE.
       MAIN-E.
         STOP RUN.
```

実行結果

```
HOGE
```

### 終了条件があるループ

その前に

#### 他のセクションのプログラムの呼び出し

```:記法
PERFORM  <呼び出しセクション名>.
```

COBOLではプログラムはセクション（他言語でいう関数）で分けることができて、これを呼び出すときに使う。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
      *
       PROCEDURE              DIVISION.
      *--ﾒｲﾝｾｸｼｮﾝ
       MAIN                   SECTION.
       MAIN-S.
         PERFORM              SUB.
       MAIN-E.
         STOP RUN.
      *--ｻﾌﾞｾｸｼｮﾝ
       SUB                    SECTION.
       SUB-S.
         DISPLAY              'HELLO WORLD'.
       SUB-E.
         EXIT.
```

実行結果

```
HELLO WORLD
```

改めて
### 終了条件があるループ

```:記法1
PERFORM  <繰り返すセクション>
  UNTIL  <終了条件>.
```

```:記法2
PERFORM
  UNTIL  <終了条件>
    <繰り返したい制御文>
END-PERFORM.
```

他言語でいう`while`文になる。

記法1は、繰り返したい処理が別セクションにあるときに使う。もし繰り返したい処理がセクション分けするほどのものでもないなら、記法2を使って、直接書いてやればいい。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  CNT                PIC 9(02)
                                VALUE 0.
      *
       PROCEDURE              DIVISION.
      *--ﾒｲﾝｾｸｼｮﾝ
       MAIN                   SECTION.
       MAIN-S.
         DISPLAY              CNT.
      *--ｷﾎｳ1ﾉﾙｰﾌﾟ
         PERFORM              SUB
           UNTIL              CNT = 10.
         DISPLAY              CNT.
      *--ｷﾎｳ2ﾉﾙｰﾌﾟ
         PERFORM
           UNTIL              CNT = 20
             COMPUTE          CNT = CNT + 1
         END-PERFORM.
         DISPLAY              CNT.
       MAIN-E.
         STOP RUN.
      *--ｻﾌﾞｾｸｼｮﾝ
       SUB                    SECTION.
       SUB-S.
         COMPUTE              CNT = CNT + 1.
       SUB-E.
         EXIT.
```

実行結果

```
00
10
20
```

記法1も記法2もやってることは同じなので、好きな方を使ってください。

### 回数が決まっているループ

```:記法1
PERFORM    <繰り返すセクション>
  VARYING  <添字>  FROM   <初期値>  BY  <増分>
  UNTIL    <終了条件>.
```

```:記法2
PERFORM
  VARYING  <添字>  FROM   <初期値>  BY  <増分>
  UNTIL    <終了条件>
    <繰り返したい制御文>
END-PERFORM.
```

他言語でいう`for`文に当たる。記法が2つあるのは上と同じで、繰り返したい処理が別セクションにあるか直接記述してしまいたいかで使い分けるため。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
       01  CNT                PIC 9(02)
                                VALUE 0.
       01  IDX                PIC 9(02).
      *
       PROCEDURE              DIVISION.
      *--ﾒｲﾝｾｸｼｮﾝ
       MAIN                   SECTION.
       MAIN-S.
         DISPLAY              CNT.
      *--ｷﾎｳ1ﾉﾙｰﾌﾟ
         PERFORM              SUB
           VARYING            IDX  FROM  0  BY  1
           UNTIL              IDX = 10.
         DISPLAY              CNT.
      *--ｷﾎｳ2ﾉﾙｰﾌﾟ
         PERFORM
           VARYING            IDX  FROM  0  BY  1
           UNTIL              IDX = 10
             COMPUTE          CNT = CNT + 1
         END-PERFORM.
         DISPLAY              CNT.
       MAIN-E.
         STOP RUN.
      *--ｻﾌﾞｾｸｼｮﾝ
       SUB                    SECTION.
       SUB-S.
         COMPUTE              CNT = CNT + 1.
       SUB-E.
         EXIT.
```

実行結果

```
00
10
20
```

やってることは上のサンプルと同じ。

## おまけ: `GO TO`

```:記法
GO TO <パラグラフ名>.
```

書いている通り、指定したパラグラフ名まで飛んで帰ってこないやつ。他の言語にもあるやつ。

```:サンプル
      *---1---------2---------3---------4---------5
       IDENTIFICATION         DIVISION.
       PROGRAM-ID.            SAMPLE.
      *
       ENVIRONMENT            DIVISION.
      *
       DATA                   DIVISION.
       WORKING-STORAGE        SECTION.
      *
       PROCEDURE              DIVISION.
       MAIN                   SECTION.
         GO TO                MAIN-E.
         DISPLAY              'HELLO WORLD'.
       MAIN-E.
         STOP RUN.
```

実行結果

```
（何もない）
```

`GO TO`で`MAIN-E`まで飛んで終了してるので`DISPLAY`は通らない。

注意するのは今までみたいに__セクション名ではなくパラグラフ名__を指定すること。いざ使うことになると間違えそう…。

## 今回はここまで

`PERFORM VARYING`とか研修の演習で全然使わないから忘れてた。

こうやって勉強して投稿するとかいう目的がないと休みの日に教材見ないからいい勉強になった。

次はデータ項目周りのことをしようかな…→[続き](https://qiita.com/zama_8722/items/adf22305cbd9033dc87e)（2019/06/29作成）