IBM i (旧AS400）の自己学習メモ　RPG Ⅳプログラミング編０２　コンパイル時配列（CTDATAを使う）

# 自己学習メモインデックス

[IBM i (旧AS400）の自己学習メモ　インデックス](https://qiita.com/You_name_is_YU/items/3964849e7803c8eaf767)

# 概要

RPG Ⅳプログラミングでコンパイル時配列を使って得意先マスタメンテナンス画面のモード切替（登録，更新，削除）を切り替えるような作りにしたかったのですが、なかなかうまく機能しなかったので、その内容をまとめてみました。
ここでも基本は**フリーフォーマット**ではなく**定位置記入方式**でRPGプログラミングをしていきます。

# 参考にした情報

以下のサイトにあるような、DIMキーワードとCTDATAキーワードを使った、コンパイル時配列の初期以下を行います。
https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_73/rzasd/ctrule.htm

```shell
      DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords++++++++++++++++++++
      DARC              S              3A   DIM(12) PERRCD(5) CTDATA
**CTDATA ARC
48K16343J64044HComments can be placed here
12648A47349K346Comments can be placed here
50B125         Comments can be placed here
```

# 試行錯誤した内容

上記サンプルコードは、インターネットで`AS400 RPG CTDATA コンパイル時配列`などのキーワードで検索すると、似たようなコードが出てきます。
そこで、ちょうど今得意先マスタメンテナンス画面の実装を進めており、マスタメンテナンスによくあるような登録，更新，削除のモードを切り替えて、そのモードの文字列を画面上に表示するために、登録，更新，削除の文字列をコンパイル時配列に格納しようと思い、以下のようにコーディングしました。

```shell
 FMT *   *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8 
        *************** Beginning of data ******************************************
0001.00  * コンパイル時配列用の変数定義                                           
0002.00 D @@MODES         S              6    DIM(3)  CTDATA PERRCD(1)              
0003.00  * コンパイル時配列の値を表示                                             
0003.02 C     @@MODES(1)    DSPLY                                                   
0006.00  * RPGの終了                                                              
0007.00 C                   EVAL      *INLR = *ON                                   
0008.00  * コンパイル時配列の初期値設定                                           
0009.00 **CTDATA @@MODES                                                            
0010.00 登録                                                                      
0011.00 更新                                                                      
0012.00 削除                                                                      
        ****************** End of data *********************************************
```

すると、SEUでEnter押下時にハイライト表示されている部分で`Form-Type entry for main procedure not valid or out of sequence.`と怒られてしまいました。

<img width="1088" alt="スクリーンショット 2020-05-04 10.10.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/d869fa6b-b537-e7c1-03f4-c9ff7aba2d97.png">

確かにサンプルコードとよく比較してみると、D仕様書の`D`キーワードの部分と`CTDATA`の２個目の`T`の部分が一致するように記述されているので、この状態ではいけないのはわかります。
だからといって、こんな風に行番号の部分を侵食してサンプルコードのような書き方をすると`User exit program processing error. `と怒られます。

<img width="1088" alt="スクリーンショット 2020-05-04 10.13.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/ec1ad4e2-20cc-0472-9ff3-ce7ef786a236.png">

いったいどうやって行番号の部分を侵食してサンプルコードのようなことができるのか。。。
参考にしたサイトにも

- 配列ソース・レコードについては、次の規則があります。
    - 各レコードの最初の配列項目は、1 桁目から始めなければなりません。
    - すべての要素は同じ長さで、中間にスペースを入れずに各要素が続いていな ければなりません。
    - レコード全体に項目を埋め込む必要はありません。 そうでない場合には、項 目の後にブランクまたは注記を含めることができます (図 1 を参照)。
    - 定義仕様書に指定された配列の要素の数が指定された項目の数より大きい場合 には、残りの要素には指定されたデータ・タイプのデフォルトの値によって埋め込みが 行われます。

という記載があります。ここでの`1桁目から`というのが、行番号のエリアにはみ出てコーディングしなければいけないということなんだろうと思ったのですが、ここでパニックになりました。

試行錯誤するうちに、SEUの画面下部にある各ファンクションキーに目を向けました。
`F11キー：Toggle`が怪しいかなと思ったのですが、SEUの画面がちょっと右にずれるだけで、期待した結果にならず、、、
F24でMore Keysを見ていると、`F20=Right`というのが出てきました。
F20キーを押下すると、SEUの画面にさらなる余白部分が登場しました！

<img width="1088" alt="スクリーンショット 2020-05-04 10.17.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/2ad91686-12ff-32f2-9423-676f56b8cdd9.png">

今までD仕様書やC仕様書の`D`や`C`がSEUの１桁目だったのに対し、F20キーを押下した後はさらに左側にスペースができて、ちゃんと１桁目っぽい領域が登場しました。

なるほど。SEUは各仕様書を記載しやすいようにわざと１桁目が仕様書を表すキーワードを指定するポジションとしていて、必要に応じて位置をずらせるようにしているんですね。

様々なサイトでコードのサンプルを載せてくれている人たちにとっては、これって**知ってて当たり前**の内容なんでしょうか？
私みたいに、これが出来ずに数時間も悩み続けてしまう素人もいるってことと、他にも同じように悩んでいる人がいたら、これを参考にしてもらえればと思ってQiita記事にまとめてみました。

# 最終的に解決したコード

まず、SEUを起動してF20キーを押下しておくこと。
※DとかCのキーワードより左側に位置するように`** CDATA @@MODES`などを定義するためには、F20キーを押下する必要がある。
そのうえで、以下のRPGをコーディング。

```shell
 FMT *  ..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8 
        *************** Beginning of data ***********************************************
0001.00       * コンパイル時配列用の変数定義                                           
0002.00      D @@MODES         S              6    DIM(3)  CTDATA PERRCD(1)              
0002.01       * インデックス変数                                                       
0002.02      D i               S              1P 0 INZ(1)                                
0003.00       * コンパイル時配列の値を表示                                             
0003.01      C                   FOR       i = 1 to 3 by 1                               
0003.02      C     @@MODES(i)    DSPLY                                                   
0003.03      C                   ENDFOR                                                  
0006.00       * RPGの終了                                                              
0007.00      C                   EVAL      *INLR = *ON                                   
0008.00       * コンパイル時配列の初期値設定                                           
0010.00 ** CTDATA @@MODES                                                                
0011.00 登録                                                                           
0012.00 更新                                                                           
0013.00 削除                                                                           
        ****************** End of data **************************************************
```

実行すると以下のように処理モードの文字列が画面上に出力されます。

```shell
DSPLY  登録 
DSPLY  更新 
DSPLY  削除 
```

- 6
    - 配列１要素あたりのサイズ
    - AとかPとか指定していないが、指定しなくてもいいみたい。
- DIM(3)
    - 配列の要素数が３となる配列を定義する。
- CTDATA
    - コンパイル時にデータをロードして初期化されることを表す。
- PERRCD(1)
    - レコードあたりの要素数を表す。
    - ここでは1を指定しているので３つの要素の１次元配列という意味合いで理解。
    - 2以上を指定すると、DIMが行で、PERRCDが列となるようなテーブルのような配列となる。

```shell
0001.00       * コンパイル時配列用の変数定義                                           
0002.00      D @@MODES         S              6    DIM(3)  CTDATA PERRCD(1)  
```

- ** CTDATA @@MODES
    - コンパイル時に配列のデータを初期化することを表すキーワード
    - @@MODESの配列を初期化することを表す。
- 登録，更新，削除
    - 特にシングルクォートやダブルクォートで囲む必要はなく、初期化用のデータを列挙する。 

```shell
0008.00       * コンパイル時配列の初期値設定                                           
0010.00 ** CTDATA @@MODES                                                                
0011.00 登録                                                                           
0012.00 更新                                                                           
0013.00 削除    
```