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

```fortran: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でコンパイルして実行すると、出力結果は、

```bash
           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コードを文字列として読み込んで、ひたすら置き換えてみました。

```julia: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コードが出力されます。
出てきたコードは、

```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()

```
となります。
これを実行すると、

```bash
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文法が出現するコードができます。とりあえず出てきたコードを見てちょくちょく直せばいいかなと思います。