前回の復習
「
第二回エディタとかエミュレータとか・・・」では、FFRPG プログラム作成に必要なツールや環境を説明しましたがいかがでしたか?FFRPG では RDi、Visual Studio Code など様々なエディタが選択可能なことはご理解いただけたと思います。今後は有償、無償のものから各プロジェクトにあったツールを皆さんで選択して、開発の生産性の向上や、他のツールとの連携を実現していきましょう。
第一回と
第二回は、FFRPG のイメージを掴んでいただくことが目的でした。今回からはいよいよプログラミングの詳細説明を行っていきます。まずはプログラムの基本構造から始めましょう!
基本構造
前回の連載「IBM i 進化論 〜知っておきたい12のこと〜」の
第四回で「プログラムの構造」を解説しました。内容は重複しますが、再度 FFRPG の基本構造を解説します。前回の連載もぜひ参照ください。
IBM i でプログラムとは、ライブラリーに登録される、タイプが *PGM のオブジェクトのことを指します。プログラムは CALL コマンドを使って実行しますが、この呼び出し方法を動的呼び出しといいます。動的呼び出しは、CALL コマンドが実行されるたびに指定したプログラムのアドレスを分析解決し、そのプログラムに制御を渡します。
FFRPG で作成できるオブジェクトは、*PGM と *SRVPGM(連載の後半で説明します)の二種類ですが、それぞれ二段階の手順を踏む必要があります。
1.ソースのコンパイル
RPG コンパイラーがソースから生成するのは *MODULE オブジェクトであり、このオブジェクトは *PGM ではないので CALL で実行することはできません。RPG ソースをコンパイルするコマンドは CRTRPGMOD です。
2.プログラムの作成
実行可能な *PGM オブジェクトを作成するには、メイン・プロシージャー(後ほど解説)を含んだ *MODULE オブジェクトが一つ以上必要です。CRTPGM コマンドを使います。
一つのソースから一つのプログラムを作成する場合は、CRTRPGMOD と CRTPGM コマンドを一度に実行してくれる CRTBNDRPG コマンドを使うと便利です。今回の連載の実習でも多くはこのコマンドを使っています。
ソースの構造
それでは、FFRPG のコードを記述する上で必要なソースの構造について更に詳しく見ていきましょう。
ソース・コードは、1つ以上のプロシージャーを必ず記述しなければなりません。プロシージャーは実行単位であり、大きく以下の二種類に分類できます。
メイン・プロシージャー
メイン・プロシージャーとは、ダイナミック呼び出し(CALL コマンド)で実行される単位で、*PGM 内にはこのメイン・プロシージャーを含む *MODULE が最低一つ存在しなければなりません。複数のモジュールを含むプログラムの場合、そのモジュールがそれぞれメイン・プロシージャーを持っていても構いません。どのモジュールのメイン・プロシージャーを呼び出すかは、プログラムの作成(CRTPGMコマンド)時に指定します。
メイン・プロシージャーは更に以下の2種類に分類できます。
- サイクル・メイン・プロシージャー
- リニア・メイン・プロシージャー
サイクル・メイン・プロシージャーは、RPG 言語が元々持っているサイクル論理を含みます。前回の連載第四回でも解説したとおり、FFRPG では全手順ファイル(命令を使用してファイルの I/O 処理を行う)の定義しかできないため、RPG サイクル論理の恩恵を受けることができません。サイクル・メイン・プロシージャーはモジュール・レベル(最初のプロシージャー定義の前の部分)に記述します。
(参考)自動読み取りファイルの定義は、定位置記入方式のファイル仕様書を使用すれば可能であり、定位置とフリーフォーム形式を混在させてプログラムを記述すれば RPG サイクル論理を利用することは可能です。サイクル・メイン・プロシージャーを最大限有効に活用するためにはこの方法を検討してください。
リニア・メイン・プロシージャーはサイクル論理を含みません。そもそも FFRPG ではサイクル論理を使用できないので、メイン・プロシージャーとしてはこちらを選択することが基本になります。icafe001 プログラムもこのタイプのメイン・プロシージャーを定義しましたね(DCL-OPT に定義した MAIN キーワードを思い出してください)。
サブ・プロシージャー
サブ・プロシージャーは静的呼び出しによって実行することができます。CALL コマンドで呼び出すことはできません(メイン・プロシージャーではないため)。静的呼び出しは動的呼び出しと違い、プログラム作成(CRTPGM コマンド)実行時にアドレスの分析解決を済ましてしまう(この処理をバインドといいます)ため、動的呼び出しよりも速く実行することが可能です。
静的呼び出しはプロシージャー間での呼び出しに使用され、実行が終了すると呼び出し元に戻ります。また引数を受け取り結果を返すことが可能なので、ユーザー定義関数として利用することもできます。複数のプログラムで共通して使用する関数をまとめてオブジェクト化したものを、サービス・プログラム(*SRVPGM)といいます。
文法解説
それでは次に、基本的な文法事項を学習していきましょう。
**free について
**free は、ソース・コードの一行目の一桁目に記述します。これにより、RPG コンパイラーは2行目以降のソース・コードが完全自由形式で記述されていると認識してコンパイルを実行することになります。完全自由形式の場合は、ソース・コードの行の長さに制限はなく、1 桁目から行末までの任意の桁に自由にステートメントを記述することができます。
ソース先頭行に **free がない場合でも、以下の制約を守れば自由形式で記述することが可能です。
- 6-7 桁目はブランク
- 自由形式ステートメントは 8 桁目から 80 桁目にコーディング
桁位置制約がある状態でのコーディングは、様々なエディタを使用する上での足かせになることが多いはずです。FFRPG で記述する場合は **free を先頭に記述し、2行目以降に制約のない状態でコーディングするようにしましょう。
コメントについて
ソース・コード内に説明文として記述されたものをコメント(または注記)と言います。コメントはコンパイラーが無視するので、プログラムを作成および保守する人の有効な情報を自由に記述することができます。
FFRPG の場合、// の後にあるすべてのテキストはコメントとみなされます。// は行の先頭で記述してもよいし、途中で記述しても構いません。先頭で記述した場合は、その行はすべてコメントとみなされ、コンパイラーが無視します。途中で記述した場合は、// の前の部分はコンパイラーが評価します。
// におけるコメントは単独行のみに有効です。複数行のコメントを記述する場合は、それぞれの行の先頭を // で始めてください。Java 言語のような複数行をコメントとする記述形式は無効なので注意しましょう。
自由形式ステートメントについて
FFRPG で記述できるものは全てステートメントと呼びます。ステートメントは以下の五種類です。
1.制御ステートメント
制御ステートメントは CTL-OPT で始まり、プログラムの生成と実行に関する情報を提供します。記述できるのはモジュール・レベルで、サブ・プロシージャー内には記述できません。
CTL-OPT に続けてキーワードを記述します。一つの制御ステートメントに複数キーワードを記述してもよいし、複数行に分けても構いません。複数行に分ける場合は、各行を CTL-OPT で始めてください。
2.ファイル記述ステートメント
ファイル記述ステートメントは DCL-F で始まり、プロシージャー内で使用するデータベースを定義します。モジュール・レベルおよびサブ・プロシージャー・レベルの両方で記述可能です。
ファイル記述ステートメントの詳細は、第6回で解説します。
3.定義ステートメント
定義ステートメントは以下で始まります。
- DCL-S
- DCL-C
- DCL-DS、END-DS
- DCL-PI、END-PI
- DCL-PR、END-PR
それぞれ、変数、名前付き定数、データ構造、プロシージャー・インターフェースおよびプロトタイプ定義を記述するために使用します。モジュール・レベルとサブ・プロシージャー・レベルの両方で記述可能です。
4.プロシージャー・ステートメント
プロシージャー・ステートメントは DCL-PROC で始まり END-PROC で終わります。リニア・メイン・プロシージャーおよびサブ・プロシージャーを定義するために使用します。
5.演算ステートメント
演算ステートメントは、プログラムのロジックを記述するために使用します。V7.3 で 60 以上の演算ステートメントがあります。モジュール・レベルとサブ・プロシージャー・レベルの両方で記述することができます。
全てのステートメントはセミコロン(;)で終わるという共通ルールがありますので注意してください。
記述順序
各ステートメントは以下の順序で記述しなければなりません。
- モジュール・レベル
1. 制御ステートメント
2. ファイル・ステートメントおよび定義ステートメント(混在化)
3. 演算ステートメント
- サブ・プロシージャー・レベル
1. プロシージャー・ステートメント
2. ファイル・ステートメントおよび定義ステートメント(混在化)
3. 演算ステートメント
4. プロシージャー・ステートメント
定義の有効範囲
定義ステートメントをどこで記述したかにより、その有効範囲が決まります。有効範囲とはその値を参照できる範囲のことです。モジュールのどこからでも参照できる定義をグローバル定義、リニア・メインおよびサブ・プロシージャー内でのみ参照できる定義をローカル定義といいます。
モジュール・レベルで定義ステートメントを記述すると、グローバル定義になります。リニア・メインおよびサブ・プロシージャー内で記述すると、ローカル変数となります。
ローカル変数とグローバル変数で同じ名前が使われた場合、そのローカル変数を定義したプロシージャー内ではグローバル変数を参照することはできません。
定義の記憶域
ローカル定義には自動記憶域が使用されます。この記憶域はプロシージャーが呼び出されている間だけ存在し、呼び出されるたびに初期化されます。
グローバル定義には静的記憶域が使用されます。この記憶域はそれを含むプログラムが実行されている間存在している記憶域です。
ローカル定義に静的記憶域を使用するようにすることも可能です。これにより、サブ・プロシージャーの呼び出しにまたがって情報を共有することができるようになります。
データ・タイプ
それでは、プログラム内で使用する変数の定義方法について解説します。変数の定義には DCL-S ステートメントを使用します。
代表的なデータ・タイプとそれを指定するキーワードは以下の通りです。
数字データ・タイプ
数字データは、整数形式(符号付き、符号なし)と、パック 10 進数およびゾーン 10 進数があります。
-1,234,567,890 をパック 10 進数、ゾーン 10 進数および符号付き整数形式で表現すると以下のようになります。
パック 10 進数(PACKED)、ゾーン 10 進数(ZONED)
パック 10 進数の場合は、最後の下位 4 ビットが F ならプラス、D ならマイナスを表します。ゾーン 10 進数は、最後の上位 4 ビットが F ならプラス、D ならマイナスになります。
同じ値を符号付き整数形式で表現すると以下のようになります。
符号付き整数形式(INT)
符号付き整数は、左端のビットが 0 だとプラス、1 だとマイナスを表します。以下を参照してください。
自動記憶域と静的記憶域の確認
ではプログラムを2つ作成してみましょう。最初のプログラムは icafe002 です。
このプログラムは、ローカル変数の記憶域について確認するものです。まずはプログラム全体を見てみましょう。
メイン・プロシージャーはリニアで作成します。メイン・プロシージャー内には incrementValue() が 3 行ありますが、これはサブ・プロシージャーを呼び出している箇所です。サブ・プロシージャーの呼び出しなので静的呼び出しです。引数を受け取るプロシージャー名の場合は、後の () の中にその引数を指定します。今回は引数を渡しませんが、その場合でも () は記述しなければなりません。
incrementValue プロシージャー内では DCL-S ステートメントを使用して 3 つの変数を定義しています。DCL-S に続いて変数名、その後にデータ・タイプを指定するキーワードが続きます。
最初の 2 つは auto_local_value と static_local_value という名前で、ともに整数形式の変数として定義しています。static_local_value 変数には static キーワードを使用しています。これによりこの変数には静的記憶域が使用されます。auto_local_value は static キーワードが指定されていないので自動記憶域が使われます。
次に dsply ステートメントで表示するメッセージを保存するための変数 dsply_message を定義しています。52 バイトの文字変数ですが、長さは dsply ステートメントの制約に則っています。
この 3 つの変数はローカル変数ですから、main プロシージャー内からは参照できないということに注意してください。
次にそれぞれの変数の値を 1 ずつ増分しています(+= 1)。そして文字リテラルと、インクリメントした変数を文字に変換するために %char 関数を使用してメッセージを作成し、変数の値を dsply で表示しています。
ローカル変数は通常呼び出しのたびに初期化されるので、auto_local_value の値は常に 1 のはずです。static_local_value は静的記憶域にあるので、前回実行時の値が残っていることになり、結果 1 ずつ増分するはずです。
それでは icafe002.rpgle としてソースを記述し、コンパイルして結果を見てみましょう。
ソース・コードの登録、コンパイル、プログラムの実行および結果の確認方法は「自習環境設定編」を参照してください。
上記結果と同じになりましたか?
getIncludeTax 関数
次は、引数を受け取って値を返すサブ・プロシージャー getIncludeTax 関数を含むプログラム icafe003 です。
サブ・プロシージャーは引数として税抜き金額(excluding_tax)、戻り値として税込み価格を戻します。引数と戻り値を定義するには dcl-pi ステートメントを使用します。
dcl-pi ステートメントは、プロシージャーの名前、キーワードの順に指定します。プロシージャー名は繰り返しになるので通常は *n を指定します。
プロシージャー名の後は戻り値のデータ・タイプを指定します。今回は税込み価格を戻すので、11 桁のパック10進数(小数点以下の桁数はなし)として戻り値を定義しています。
続いて受け取る引数を定義しています。変数名、データ・タイプ、キーワードと定義しています。この変数はローカル変数です。
value キーワードは引数を値で受け取ることを指定します。今回のサンプルでは、引数として 2200(数値リテラル)を直接指定しているので value キーワードが必要です。
サプ・プロシージャー getIncludeTax で取得した税抜き金額から税込み価格を計算しているのが return ステートメントの後の式です。この式の結果が戻り値として呼び出し元に返されます。今回は直接計算式を記述していますが事前に計算を行った結果の値を保持している変数を指定しても構いません。
上記の式は exluding_tax X (1 + SALES_TAX) を意味します。四則演算はそれぞれ + – * / を使用して記述します。
税込み価格は 2018 年現在、消費税率が 8% なので 1.08 を掛ければよいのですが、将来の税率変更を考慮して名前付き定数として 0.08 に SALES_TAX という名称を設定して式の中で使用しています。
この定数はモジュール・レベルで定義しているので、モジュールのどこからでも参照できることに注意してください。
それでは icafe003.rpgle としてソースを記述し、コンパイルして結果を見てみましょう。
今回は税抜き価格 2,200 円を引数として渡しているので、計算結果の税込み価格は 2,376 円ですよね。
将来消費税率が 10% になったときは、モジュール・レベルで定義している SALES_TAX の値を変更してコンパイルすれば対応できます。
もちろん軽減税率制度は考慮されていませんので、その際は追加のロジックが必要になりますね。
終わりに
いかがでしたか?今回は、ソース・コードの基本構造と文法の基礎、変数の定義と記憶域の確認、サブ・プロシージャーを使った関数の作成を行いました。FFRPG の基本的な部分についておおまかに理解できたのではないかと思います。
次回からはデータベースを処理するプログラムを 4 回に渡って解説する予定です。IBM i には OS 標準のデータベース機能があり、その仕組みを理解することで記述できる処理の範囲が格段に広がります。最初の 2 回は SQL による処理、後半の 2 回はネイティブ・アクセスによる処理を扱います。
どうぞお楽しみに!
著者プロフィール