-
-
Save rafaqz/fede683a3e853f36c9b367471fde2f56 to your computer and use it in GitHub Desktop.
| #= | |
| This julia script converts fortran 90 code into julia. | |
| It uses naive regex replacements to do as much as possible, | |
| but the output WILL need further cleanup. | |
| Known conversion problems such as GOTO are commented and marked with FIXME | |
| Most variable declaration lines are entirely deleted, which may or | |
| may not be useful. | |
| To run from a shell: | |
| julia fortran-julia.jl filename.f90 | |
| Output is written to filename.jl. | |
| =# | |
| using DataStructures | |
| # Regex/substitution pairs for replace(). Order matters here. | |
| replacements = OrderedDict( | |
| # Lowercase everything not commented | |
| r"^(?!.*!).*"m => lowercase, | |
| # Lowercase start of lines with comments | |
| r"^.*!"m => lowercase, | |
| # Remove '&' multiline continuations | |
| r"\s*&\s*" => "", | |
| # Comments use # not ! | |
| "!" => "#", | |
| # Powers use ^ not ** | |
| "**" => "^", | |
| # Only double quotes allowed for strings | |
| "'" => "\"", | |
| # DO loop to for loop | |
| r"do (.*),(.*)" => s"for \1:\2", | |
| # Spaces around math operators | |
| r"([\*\+\/=])(?=\S)" => s"\1 ", | |
| r"(?<=\S)([\*\+\/=])" => s" \1", | |
| # Spaces around - operators, except after e | |
| # r"([^e][\-])(\S)" => s"\1 \2", | |
| r"(?<!\W\de)(\h*\-\h*)" => s" - ", | |
| # Space after all commas | |
| r"(,)(\S)" => s"\1 \2", | |
| # Replace ELSEIF/ELSE IF with elseif | |
| r"(\s+)else if" => s"\1elseif", | |
| # Replace IF followed by ( to if ( | |
| r"(\s+)(elseif|if)\(" => s"\1\2 (", | |
| # Remove THEN | |
| r"([)\s])then(\s+)" => s"\1\2", | |
| # Relace END XXXX with end | |
| r"(\s+)end\h*.*" => s"\1end", | |
| # Replace expnent function | |
| r"(\W)exp\(" => s"\1exp(", | |
| # Reorganise functions and doc strings. This may be very project specific. | |
| r"#\^\^+\s*subroutine\s*(\w+)([^)]+\))\s*(.*?)#\^\^\^+"sm => | |
| Base.SubstitutionString("\"\"\"\n\\3\"\"\"\nfunction \\1\\2::Void"), | |
| r"\#\^\^+\s*real function\s*(\w+)([^)]+\))\s*(.*?)\#\^\^\^+"sm => | |
| Base.SubstitutionString("\"\"\"\n\\3\"\"\"\nfunction \\1\\2::Float64"), | |
| # Don't need CALL | |
| r"(\s*)call(\h+)" => s"\1", | |
| # Use real math symbols | |
| "gamma" => "Γ", | |
| "theta" => "Θ", | |
| "epsilon" => "ϵ", | |
| "lambda" => "λ", | |
| "alpha" => "α", | |
| # Swap logical symbols | |
| ".true." => "true", | |
| ".false." => "false", | |
| r"\s*\.or\.\s*" => " || ", | |
| r"\s*\.and\.\s*" => " && ", | |
| r"\s*\.not\.\s*" => " ! ", | |
| r"\s*\.eq\.\s*" => " == ", | |
| r"\s*\.ne\.\s*" => " != ", | |
| r"\s*\.le\.\s*" => " <= ", | |
| r"\s*\.ge\.\s*" => " >= ", | |
| r"\s*\.gt\.\s*" => " > ", | |
| r"\s*\.lt\.\s*" => " < ", | |
| # Remove (expression) brackets after if | |
| # r"if \((.*)\)(\s*\n)" => s"if \1\2", | |
| # Add end after single line if with an = assignment | |
| r"if\s*(.*?) = (.*?)(\n)" => s"if \1 = \2 end\3", | |
| # Format floats as "5.0" not "5." | |
| r"(\W\d+)\.(\D)" => s"\1.0\2", | |
| # Tab to 4 spaces | |
| r"\t" => " ", | |
| # Relace suberror with error and mark for fixup | |
| r"(\W)suberror\((.*?),.*?\)" => s"\1 error(\2)", | |
| # Mark #FIXME the various things this script can't handle | |
| r"(write|goto|while\s)" => s"#FIXME \1", | |
| ) | |
| # Patterns to remove | |
| removal = [ | |
| # Trailing whitespace | |
| r"\h*$"m, | |
| # Variable declarations | |
| r"\n\s*real\s.*", | |
| r"\n\s*real, external\s.*", | |
| r"\n\s*integer\s.*", | |
| r"\n\s*implicit none", | |
| r"\n\s*logical\s.*", | |
| # Import statements | |
| r"\n\s*use\s.*", | |
| ] | |
| # Load the file from the first command line argument | |
| filename = string(ARGS[1]) | |
| global code = read(filename, String) | |
| # Process replacements and removals. | |
| for (f, r) in replacements | |
| global code = replace(code, f, r) | |
| end | |
| for r in removal | |
| global code = replace(code, r, "") | |
| end | |
| println(code) | |
| # Write the output to a .jl file with the same filename stem. | |
| stem = split(filename, ".")[1] | |
| outfile = stem * ".jl" | |
| write(outfile, code) | |
| #= | |
| Copyright (c) 2022 Rafael Schouten | |
| Permission is hereby granted, free of charge, to any person obtaining a copy | |
| of this software and associated documentation files (the "Software"), to deal | |
| in the Software without restriction, including without limitation the rights | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| copies of the Software, and to permit persons to whom the Software is | |
| furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in all | |
| copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| SOFTWARE. | |
| =# |
Its honestly a regex hack I havent used since 2019... there will be edge cases and version of Fortran it really won't work on, and I'm concerned that would generate a lot of maintenance. But if someone wants to bundle it into a package go for it.
Makes sense.
I have used your script as a base and modified it to successfully convert a f77 legacy code base (about 9000 pages long) to Julia scripts. Pretty awesome, thank you @rafaqz for creating and sharing your script!
Hi,
Got this "ERROR: LoadError: UndefVarError: readstring not defined" when tried to run the code.
Any suggestion?
Thanks
Look at the comment above fixing this problem ;)
Thanks @rafaqz
Hi @rafaqz, i was trying to understand how to define/declare the scope of a variable. I'm used to fortran but i'm having some trouble to understand how to define the scope of a local "i" in a for loop, i got this: "UndefVarError: i not defined". I want to run a basic application of lagrange interpolation.
Thanks in advance.
This is a good question for the Julia discourse! Read this https://discourse.julialang.org/t/please-read-make-it-easier-to-help-you/14757 and make a post including a minimum working example of your code and people will help you.
I have also updated the gist to include the advice from the comment, so it should work now without change
@rafaqz could you please add a licence header (MIT?) so I can include this in another project and credit you?
@dmey I added an MIT license to the text
@rafaqz I am getting error ERROR: LoadError: MethodError: no method matching replace(::String, ::Regex, ::typeof(lowercase)) I am using Julia1.7
I was able to solve simply by switching to replace(code, f=> r)
Hi all, I've piled one translator here
https://github.com/denius/FortranTranspiler.jl/blob/main/src/FortranTranspiler.jl
Probably should make this into a package.