Computing Science Technical Report No. 149
A Fortran-to-C Converter
S. I. Feldman, D. M. Gay, M. W. Maimone, N. L. Schryer
Last updated March 22, 1995. Originally issued May 16, 1990.
による.
以下に、Fortran特有の機能とそれに対する対策を述べる。
(1)字句解析が難しい
72欄のカード形式,スペースの有無が自由,キーワードが無い,変数は宣言しなくてもいい,関数呼出しと配列要素が同じ形,など字句解析を難しくする仕様がある.
これについては,手書きの字句解析プログラムで,構文解析しやすいトークン列に変換する.OMNIのFortranフロントでもそのようにしており,それを参考にする.
字句解析では以下の処理をする.
・継続行はまとめて1つの文とする.
・文の分類をする(代入文,do文,FORMAT文など)
ただし,代入文と文関数定義文の区別は出来ない(その区別は構文解析時).
・代入文と文関数定義文にはletというキーワードがついていることにする.
・各文の最後にはend_of_statementトークンがついていることにする.
・各文の先頭には「ラベルなし」トークンか「ラベル定義」トークンかがついている
これはOMNIにはない.
(2)ENTRY 文
サブプログラムが入口を複数もつ.例えば,次のプログラム
SUBROUTINE SUB1(X, Y)
...
10 ENTRY SUB2(X, Z)
...
END
は
SUBROUTINE SUB1(X, Y)
CALL SUB12(1, X, Y, dummy)
END
SUBROUTINE SUB2(X, Z)
CALL SUB12(2, X, dummy, Z)
END
SUBROUTINE SUB1_(J_, X, Y, Z)
IF ( J_ .EQ. 2 ) GO TO 10
...
10
...
END
に変換する.実際には次のようなものに変換することにした.
void SUB1(float *X, float *Y){
int i_ = 1; float dummy_;
SUB12(&i_, X, Y, &dummy_);
}
void SUB2(float *X, float *Z){
int i_ = 2; float dummy_;
SUB12(&i_, X, &dummy_, Z);
}
void SUB1_(int *J_, float *X, float *Y, float *Z){
switch(*J_) {
case 1: goto L_SUB1;
case 2: goto L_SUB2;
L_SUB1:
...
L_SUB2:
...
}
FUNCTIONの場合はもう少し複雑になる。
FUNCTION f(r)
f = r
ENTRY g(s)
g = f + s
END
は
float f(float *r){
float _f, dummy_;
int i_ = 1;
_f = f_(&i_, r, &dummy_);
return _f;
}
float g(float *s){
float _g, dummy_;
int i_ = 2;
_g = f_(&i_, &dummy_, s);
return _g;
}
float f_(int *i_, float *r, float *s){
float _f_;
switch(*i_){
case 1: goto L_f;
case 2: goto L_g;
}
L_f: _f_ = *r;
L_g: _f_ = _f_ + *s;
return _f_;
}
に変換する.実は,Fortranの規格では,fとgの型が違ってもよいことになっているが,それを実現するのは簡単ではない.f2cでも考えていないようなので(確認はしていません),それは考えないことにする.実現するとしたら,次のようにすることが考えられる.
FUNCTION f(r)
INTEGER f;
f = r
ENTRY g(s)
g = f + s
END
は
int f(float *r){
int _f, dummy_;
int i_ = 1;
_f = (int)f_(&i_, r, &dummy_);
return _f;
}
float g(float *s){
float _g, dummy_;
int i_ = 2;
_g = (float)f_(&i_, &dummy_, s);
return _g;
}
union{int, float} f_(int *i_, float *r, float *s){
float float_; int int_;
switch(*i_){
case 1: goto L_f;
case 2: goto L_g;
}
L_f: int_ = *r;
L_g: float_ = int_ + *s;
switch(*i){
case 1: return int_;
case 2: return float_;
}
}
に変換する.
(3)CALL 文のalternate return specifier
引数で指定された文番号のところにreturnすることが出来る.たとえば,次のようなプログラムが書ける.
CALL SUB(A, *100, *200) SUBROUTINE SUB(X, *, *)
RETURN 2
(ステートメント200にリターン)
これは,次のようなプログラムに変換する.
GO TO (100, 200) ,SUB(A) INTEGER FUNCTION SUB(X)
SUB = 2
RETURN
Cの表現では,次のようになる.
switch(SUB(A)){ int SUB(X) {
case 1: goto L_100; return 2;
case 2: goto L_200;
} }
(4)statement function
1つの文で関数を定義することが出来る.それを1つのサブプログラムとすることも出来るが,ここでは,文関数呼出しはインライン展開することにする.
(5)ASSIGN文
ASSIGN s TO i sは文番号,iは整数変数
は
i = s
に変換する.これは,通常の整数の代入文である.次の(6)と合わせて機能する.
(6)assigned GO TO 文
GO TO i [ (s [,s]...)] sは文番号,iは整数変数
は
switch(i){
case s: goto L_s (最初のsは整数定数.後ろのL_sはラベル)
. . .
}
に変換する.
(7)computed GO TO 文
GO TO (s [,s]...) i sは文番号,iは整数変数
は
switch(i){
case 1: goto L_s1
case 2: goto L_s2
. . .
}
に変換する.
(8)算術IF文
IF (e) s1, s2, s3
は
if (e < 0) goto L_s1
if (e == 0) goto L_s2
else goto L_s3
に変換する.
(9)COMPLEX型
複素数型はstruct型として表現することにする.複素数型に関する演算は実数型の演算に分解する.たとえば,
COMPLEX c, d
c = c + d
は
struct complex { float re; float im } c, d;
c.re = c.re+ d.re;
c.im = c.im + d.im;
に変換する.
(10)expressionの評価
ある部分を評価しなくてもexpressionの値が決まる場合,その部分を評価しなくてもよい.評価の順序は,mathematically equivalent expressionに変えて評価してよい.ただし,カッコで囲まれたexpressionは一つのentityとして扱わねばならない.
HIRには,Fortranのこの規則を考慮してカッコで囲まれたexpressionであることを表すencloseという演算子が用意されているから,それを使うことにする.
(11)**演算子
べき乗演算子をHIRに追加することはしない.べき乗演算子は,一般には関数呼出しに変換する.いくつかの定数整数べき乗は掛け算にインライン展開することにする.
(12)COMMON
COMMONブロックはstructで表現する.
(13)EQIUVALENCE
EQIUVALENCEはunionで表現する.
(14)多次元配列のメモリ配置
たとえば,
DIMENSION k(10, 30)
...k(3, 7)
は
int k[30][10];
... k[6][2]
のSymとHIRに相当するものに変換する.
(15)SAVE文
SAVE [a [,a]...]
サブルーチンからリターンしても,ここにリストした変数の値は保持される.そのために,SAVE文にある変数をstaticとする.
(16)引数について
call by addressであるから(call by valueも許す処理系はあるが,規格(http://www.fortran.com/fortran/にある)には入っていない),引数はポインタ型に変換する.実引数が変数以外の式である場合はダミーの変数への代入文を作り,その変数のアドレスを実引数とする.
(17)入出力文とFORMAT文
open,close,backspace,rewindはライブラリ呼出しの形とし,FORMATは文字列の引数として入出力のライブラリ(f2cのlibF77とlibI77)を呼び出す形とする.入出力のリストとしてのループはループ文に展開する.
(18)配列の宣言
配列の宣言が,どこからでもいい a(-10:10,-1:10) b(100:105).サブプログラムでは仮引数の配列としてadjustable array ,assumed-size array(最後の上限が*)などが使える.
Fortranでの配列の宣言の情報(仮引数などを含んだ式による上下限の指定など)をすべて扱えるようにHIRのVector型を拡張する.
(19)文字列と部分文字列
文字列の宣言でcharacter c*500,d*100という書き方をすると,cは長さ500の文字列型である.文字列型を関数の戻り型とすることもできる.
代入文
c(1:200)=c(2:201)
で部分文字列の代入が出来る.
(20)data文やblock data文
部分文字列やimplied-DOリストにも初期値設定できる.初期値としてn*cの形でn個の定数を表現できる.
(21)implicit 宣言,ijklmn規則
宣言なしでも変数の型が決まる.
(22)PARAMETER文
定数名の宣言
(23)PAUSE文,STOP文
ライブラリ呼出しにする.PAUSEのデフォルトは何もしないライブラリ.STOPはプログラム終了.
(24)記憶単位
INTEGER,REAL,LOGICALは1数値記憶単位(numeric storage unit)を占める
DOUBLE PRECISIONは2数値記憶単位を占める
COMPLEXは2数値記憶単位を占める(最初の1単位が実数部,次が虚数部)
CHARACTER(1文字)は1文字記憶単位を占める
INTEGERはHIRのint(LIRのI32)とする
REALはHIRのHIRのfloat(LIRのF32)とする
DOUBLE PRECISIONはHIRのdouble(LIRのF64)とする
COMPLEXは2つのfloatとする
CHARACTER(1文字)はHIRのchar(LIRのI8)とする
LOGICALはHIRのbool(LIRのI32)とする
(25)文字列定数
Cでは,"abc"は長さ4の文字配列.Fortranでは'abc'は長さ3