Edited at

Fortran90のコードをJuliaのコードに変換するJuliaのコード その1

プログラムを生成するプログラム、という響きに何か憧れを感じています。

ということで、Fortran90のコードをJulia 1.1のコードに自動変換するコードを書いてみました。

結果的に、Juliaでの文字列の扱いを勉強できました。


Fortran90について

古い言語ですが、スーパーコンピュータなどの大規模数値計算では現役バリバリで使われています。

Fortran90の全ての言語仕様に対応することは難しいので、ふんわりと書き換えて、残った部分は手作業で直すような運用が良いかなと思います。


対応した文法


  1. Doループ

  2. subroutine

  3. function

  4. write文

  5. call文

  6. integerとreal(8)


  7. !によるコメントアウト

とりあえずよく出てきそうなものを重点的に変換できるようにしました。


現時点で未対応


  1. If文

  2. 配列

  3. module文

  4. complexやcharacterなど

  5. 再帰的なfunctionなど

1-4までは、時間のあるときに対応できたらと思います。5はFortran90の仕様なのか覚えていません。コードの中にそんなになければ、1-4まで対応していれば手作業でぱぱっとできるかと思います。なお、continue文などがあるFORTRAN77には対応する予定はありません。FORTRANのコードはまずfortranのコードに変えるのが良いでしょう。


Juliaについて

Juliaのバージョンは1.1を使いました。


有用だった関数



  • readlines :ファイル名を入れると配列として読み込んでくれます


  • countlines:ファイルに含まれる行の数を数えます。


  • occursin("program",data_i):data_iという文字列の中にprogramが含まれていればtrueを返します。


  • split(data_i):よしなに文字列data_iを区切ってくれます


  • split(ut,","):ut,で区切ってくれます。二個目の変数に好きな文字列を入れればそれで区切ってくれます。


  • filter!(x -> x != nstring,u): 文字列が格納された配列uから、文字列nstringを削除した配列を作ります。


  • replace(data_i, '*'*'*' => '^'): 文字列data_iに含まれている**^に置き換えます。アスタリスク*`*`で指定できました。


  • join(u," "):文字列を要素とする配列の中身をでつなげて一つの文字列にします。空白の代わりに任意の文字列を使うことが可能です。


  • findfirst("""(""",ut): 文字列utの中で(が何文字目にあるかを返します。


  • "\t"^(numtab):numtab回、\t(つまりタブ)を繰り返します。endの数やfunctionやsubroutineの数を数えて、コードにインデントがつくようにしています。


  • lowercase(data):文字列dataを全て小文字に変換します。Fortran90では大文字小文字を区別しませんが、コードの書き方としてはどちらも使う場合がありますので、ここは統一してからJuliaコードに変換しました。


変換したFortran90コード

最初のテストなので、比較的簡単なコードにしました。無駄にスペースや大文字などをいれています。


test.f90

!!!!!!!!!!!!!!

!コメントをつけてみよう
!どんな関数だろうか
!!!!!!!!!!!!!!!!
program main
implicit none
real(8)::a,b
a = 10d0
b = 1d-5
call test()
call test2(a, b)
call test3(a , b)!これはtest3です。
end

subroutine Test()
implicit none
real(8)::a,b
a = 10
return
end

subroutine test2(a,b)
implicit none
real(8)::a,b
b = a*10
return
end

subroutine test3(a , b)
implicit none
real(8)::a,b
integer::i
b = a*10
do i = 1,10
call test2(a ,b)
write(*,*) i
end do
do i = 1,10,2
write(*,*) i**2,a , b, 10*b
end do

return
end

function testfunc (a,b )
implicit none
real(8)::a,b,testfunc
a = 10
return
end


gfortranでコンパイルして実行すると、出力結果は、

           1

2
3
4
5
6
7
8
9
10
1 10.000000000000000 100.00000000000000 1000.0000000000000
9 10.000000000000000 100.00000000000000 1000.0000000000000
25 10.000000000000000 100.00000000000000 1000.0000000000000
49 10.000000000000000 100.00000000000000 1000.0000000000000
81 10.000000000000000 100.00000000000000 1000.0000000000000

のようになります。


Fortranコードを変換するJuliaコード

Fortranコードを文字列として読み込んで、ひたすら置き換えてみました。


test.jl

function loadfile(filename)

data = readlines(filename)
numofdata = countlines(filename)
juliacode = []
numtab = 0
for i=1:numofdata
code,addtab= f2jl(data[i])
#println(code)
if addtab < 0
numtab += addtab
tabs = "\t"^(numtab)
code = tabs*code
else
tabs = "\t"^(numtab)
code = tabs*code
numtab += addtab
end

push!(juliacode,code)
println(code)
end
push!(juliacode,"main()")
println("main()")
return
end

function f2jl(data)
data_i = lowercase(data)
data_i = replace(data_i, '!' => '#')
addtab = 0
if occursin("program",data_i)
code = "function main()"
addtab = 1

elseif occursin("subroutine",data_i)
nstring = "subroutine"
u = split(data_i)
ut = join(filter!(x -> x != nstring,u)," ")
code = "function "*ut
addtab = 1
elseif occursin("function",data_i)
nstring = "function"
u = split(data_i)
ut = join(filter!(x -> x != nstring,u)," ")
ints = findfirst("""(""",ut)
inte = findfirst(""")""",ut)
functionname = strip(ut[1:ints[1]-1])
code = "function calc_"*functionname*ut[ints[1]:inte[1]-1]*","*functionname*")"
addtab = 1
elseif occursin("call",data_i)
nstring = "call"
u = split(data_i)
ut = join(filter!(x -> x != nstring,u)," ")
code = ut
elseif occursin("end",data_i)
code = "end"
addtab = -1
elseif occursin("do",data_i)
nstring = "do"
u = split(data_i)
ut = join(filter!(x -> x != nstring,u)," ")
ut = split(ut,",")
len = length(ut)
if len == 2
code = "for "*ut[1]*":"*ut[2]
elseif len == 3
code = "for "*ut[1]*":"*ut[3]*":"*ut[2]
else
println("error!")
println(ut)
stop()
end
addtab = 1
else
code = f2jl_detail(data_i)
end
return code,addtab
end

function f2jl_detail(data_i)
data_i = replace(data_i, '*'*'*' => '^')
data_i = replace(data_i, 'd'*'-' => 'e'*'-')

if occursin("implicit",data_i)
return ""
elseif occursin("real",data_i) && occursin("::",data_i)
return ""
elseif occursin("integer",data_i)
return ""
end
if occursin("write",data_i)
nstring = "write"
u = split(data_i)
ut = join(filter!(x -> x != nstring,u)," ")
ints = findfirst("""(""",ut)
inte = findfirst(""")""",ut)
ut = ut[inte[1]+1:end]
ut = replace(ut, "," => """, " " ,""")
code = "println"*"""("""*ut*""")"""
return code
end
if occursin("=",data_i)
for i=0:320
data_i = replace(data_i, 'd'*string(i) => 'e'*string(i))
end
end

u = split(data_i)
ut = join(u," ")
code = ut
return code
end

filename = "test.f90"
loadfile(filename)


このコードを実行すると標準出力にJuliaコードが出力されます。

出てきたコードは、


test2.jl

##############

#コメントをつけてみよう
#どんな関数だろうか
################
function main()

a = 10e0
b = 1e-5
test()
test2(a, b)
test3(a , b) #これはtest3です。
end

function test()

a = 10
return
end

function test2(a,b)

b = a*10
return
end

function test3(a , b)

b = a*10
for i = 1:10
test2(a ,b)
println( i)
end
for i = 1:2:10
println( i^2, " " ,a , " " , b, " " , 10*b)
end

return
end

function calc_testfunc(a,b ,testfunc)

a = 10
return
end
main()


となります。

これを実行すると、

1

2
3
4
5
6
7
8
9
10
1 10.0 100.0 1000.0
9 10.0 100.0 1000.0
25 10.0 100.0 1000.0
49 10.0 100.0 1000.0
81 10.0 100.0 1000.0

となります。Fortran90のコードと同じ結果ですね。


未対応な文法がある場合

なお、まだ対応していないFortran90の文法に遭遇した場合、そのまま書き出すことになるので、JuliaコードにいきなりFortran90文法が出現するコードができます。とりあえず出てきたコードを見てちょくちょく直せばいいかなと思います。