LoginSignup
3
1

More than 5 years have passed since last update.

COBOLで作るチェックインタプリタ用の仮想マシンコード、そして定数と変数:COBOLでインタープリタ

Last updated at Posted at 2015-04-29

平易な文からアセンブラ風に置き換えることで、COBOLで作るチェックインタープリタ用の仮想マシンコード、そして定数や変数の理解を深める。

COBOLで作るインタープリタ用の仮想マシンコードについて

まず、元のIF文から。

元のIF文
IF 従業員レコード.住所.郵便番号 = "100-****"
THEN
  メッセージ出力 "住所は東京都千代田区"
END-IF

次に、これをアセンブラ風に置き換えてみる。

アセンブラ風の記述その1
01:PUSH 変数.従業員レコード.住所.郵便番号のアドレス
02:PUSH 定数."100-****"へのアドレス
03:EQ   
04:JPZ  07
05:PUSH 定数."住所は東京都千代田区"
06:PUT メッセージ出力エリア
07:END

今度は、元のIF文を抽象化してみる。

元のIF文を抽象化
IF IN1.6-8 = 定数."100-****"
THEN
 メッセージ出力 OUT.1 定数."住所は東京都千代田区"
END-IF

抽象化したIF文をアセンブラ風に置き換える。

抽象化したIF文のアセンブラ風記述
00001:PUSHV 10000600008
00002:PUSHL "100-****"
00003:EQ___ 
00004:JPZ__ 00007
00005:PUSHL "住所は東京都千代田区"
00006:PUT__ 00001
00007:END__

IN1.6-810000600008に置き換わっています。この意味は、入力レコード1番の6桁目から8桁を切り出した文字列です。

これが、今回COBOLで作ろうとしているチェックインタープリタの仮想マシンコードです。このようなコードをテキストファイルに記述して、チェックインタープリタに読み込ませて使います。

変数と定数

仮想マシンコードがチェックインタープリタに読み込まれると、仮想マシンコードのオペレータとオペランドがインタープリタ内部で管理しているメモリ領域に設定されます。

チェックインタープリタ内部のメモリ領域に設定された変数と定数

アドレス 01 02 03 04 05 06 07 08 09 10
00001
00011
00021

"10000600008"がメモリ領域のアドレス00001番地から11桁設定されています。黒四角は文字列を区切るデリミタです。区切り文字は何でもよいのですが、HIGH-VALUEを使います。
定数"100-****"は、メモリ領域の00013番地から8桁設定されています。
定数"住所は東京都千代田区"は、メモリ領域の00023番地から10桁設定されています(漢字の定数なので1byteで済まないのですがイメージとしてとらえてください)。

この00001番地00013番地00023番地がインタープリタ内で実行される仮想マシンコードのオペランドに設定され、実行されます。

チェックインタープリタ内部メモリ領域に変数、定数が設定され、それが実行時アドレスに設定された状態
00001:PUSHV 00001
00002:PUSHL 00013
00003:EQ___ 
00004:JPZ__ 00007
00005:PUSHL 00023
00006:PUT__ 00001
00007:END__

変数のメモリ設定で悩んだこと

当初結構悩んだのは、変数をメモリに設定するのはどのタイミングで設定して、どうやって扱うべきなんだろうか?ということです。字句解析と構文解析は難しいのでCOBOLインタープリタの対象外にしました。ということは、仮想マシンコードのテキストファイルを作る段階では、まだ変数のメモリ領域確保はできないこということになります。

次に考えられるタイミングは、仮想マシンコードのテキストファイルをチェックインタープリタに読み込むときです。この時に、変数名をメモリ領域に覚えこませます。これなら行けそうです。

次にその変数が指し示すメモリ領域確保をどうやるかです。チェックインタープリタでは文字列を扱います。その変数の桁数分メモリ領域の確保をするのですが、ここが結構はまりました。

教科書的には変数名が出てきたらメモリ領域を確保するとあるのですが、チェックインタープリタで扱う変数(入力レコード)は、チェックインタープリタの外部から与えられるので、変数用にインタプリタ内にメモリ領域を確保するとメモリを圧迫しますし、外部-内部の文字列移送の処理時間がばかにならないと思いました。

ということで、外部から与えられる入力レコードの開始桁位置への参照をインタープリタ内のメモリ領域で管理することとしました。

では、それをどうやって扱うかですが、その変数名自体が入力レコードの番号と、開始桁位置と切り出すべき桁数を表すようにしてしまえば、その変数名=文字列をメモリ領域に設定してあるので、それを見ればどの入力レコードを見て文字列を切り出せばよいか判断できます。

当初考えた実装は、変数名を管理する配列で実装することです。配列の1要素はHashMapで、キーが変数名、値が開始桁と桁数です。配列の添え字が変数名の変わりになります(アドレスになります)。

チェックインタープリタにプログラム行が読み込まれる都度判断して、その行がPUSHVなら、オペランドを変数名管理配列に格納して、その戻り値としてこの配列の添え字を実行時アドレスとしてオペランドに設定したらどうかなぁと考えていました。

ただ、HashMapをCOBOLで実装するのが面倒だったのと、変数のIN1.6-810000600008であり、単なる文字列とみなすことができ、定数と同じ管理の仕方で十分だろうと考えなおしました。つまり、定数同様に変数10000600008もインタプリタ内部メモリ領域に設定して、その文字列の先頭アドレスをPUSHVのオペランドに設定すればよいと。

定数のメモリ設定で悩んだこと

今思えばこうすればよかったと思っているのが、仮想マシンコードのテキストファイル内に、データ定義部を設けてそれを参照するやり方の方が教科書的だしスマートだったなぁと。

平文から仮想マシンコードへのコンパイルの際に、変数(入力レコードの参照を文字列として表現)、定数をコンパクトな形(ダブりなく)で、データ定義部に設定できるからです。

上記の「変数のメモリ設定で悩んだこと」で採用したやり方では、例えば1行目でPUSHV 10000600008とあり、10行目にもPUSHV 10000600008が登場すると、10000600008がメモリ領域に2か所別物として設定されます。本来同じ変数(入力レコードの参照)なのにです。

一方で、上記方式は仮想マシンコードを見たときにやろうとしていることがまだ読みやすいですが、データ定義部の方式だと、そのデータ定義部を参照してからでないとコードが読めないので、コードの理解が面倒です。

可読性を保ちつつコンパクトなメモリ活用が今後の課題です。

データ定義部を持ったイメージ。可読性は劣る。
00001:PUSHV 00001
00002:PUSHL 00013
00003:EQ___ 
00004:JPZ__ 00007
00005:PUSHL 00023
00006:PUT__ 00001
00007:END__
00008:DC___ 10000
00009:DC___ 60000
00010:DC___ 8100-
00011:DC___ ****
00012:DC___ 住所は
00013:DC___ 東京都
00014:DC___ 千代田
00015:DC___ 区"
3
1
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
3
1