Findent: indent and relabel Fortran sources
Findent: what does it do?
Findent indents Fortran sources. When I was playing at https://www.surfsara.nl, I parallelized a number of Fortran programs. The first things I used to do were:
- convert to Fortran90, using modules, to enable the compiler to detect silly mistakes
- indent the sources
For indenting I used gvim, which does a reasonable job, but far from perfect. Understandable: parsing Fortran with vimscript is not easy, especially if it has to be fast.
Desirable properties of a Fortran indenter
I think a Fortran indenter should have the following properties:
- indents correctly old fashioned and modern Fortran
- indents fixed and free format, the amount of indent decided by the user
- indenting is increased at 'subroutine', 'module', 'function', 'select case', 'do', and so on, and decreased as appropriate
- is capable to convert from fixed format to free format, and the other way around
- is capable to convert 'end' statements to 'end subroutine', 'end program' or 'end module' statements to facilitate the conversion from Fortran-77 to Fortran-90
- does not 'beautify' the source: if I put 3 spaces somewhere in the middle of a line, they should be preserved
- does not break long lines: every decent compiler can handle long lines
- is capable to process 'tabbed input' (i.e. a tab is used in the first 6 characters of a fixed-format Fortran line)
- indents correctly from Fortran-4 to Fortran-2008 or later
- it works on a line-per-line basis, so that the indenting can start anywhere
The indenter does not have to parse a Fortran statement completely, but it should not be easily fooled by constructs like:
do 10 i=1.10 ! this is not a do statement, but an assignment ! to the variable 'do10i' done = 20 ! this is an assignment to the variable 'done' subroutine(x) = 20 ! not a subroutine statement realfunctioncalc(x) ! must be recognized as a function statement do 10 i=1,n do 10 j=1,m ... 10 continue ! this must be recognized as ending two loops #ifdef one ! the indenter should be capable to see that this is only one do statement, ! and not indent twice do i =1,1 #else do i=1,n #endif
And it would be nice if the program runs in Linux, Windows and OS X.
I decided to try to write a Fortran indenter, using the requirements above. It took me more time than I thought, but in the end I am very happy with the result, especially because the easy interoperability with gvim. I did my best to fulfil all requirements above. The program is developed using Linux (Ubuntu), g++, flex and bison.
- findent supports Fortran 2018, and is validated against all constructs in 'Modern Fortran explained, Incorporating Fortran 2018, Metcalf; Reid; Cohen'
- findent supports vintage Fortran standards from Fortran IV and Fortran66 on
- findent optionally relabels (= renumbers labels) Fortran sources
- indenting speed: > 100K lines/second
- relabeling speed: > 40K lines/second
As a bonus, I wrote a graphical front end, written in Java, and baptised 'jfindent' . (It appears that practically nobody is interested in jfindent).
Using findent in vim, gedit and emacs
Findent optionally outputs configuration files for usage
with vim or gedit and emacs, see
Using findent in eclipse
I was not able to find out how to incorporate findent in eclipse and would really appreciate it if somebody could help me out.
Using findent to create dependencies for 'make'
The creation of dependencies in a Fortran project using modules is kind of a problem, see also the f90dep entry in this web site. Since findent has the ability to recognize definitions of (sub)modules and the usages thereof, it was tempting to let findent optionally list these dependencies. See the man page and look for 'Dependencies'. Findent is able to produce a shell script that can be used as a starting point for usage in your large f90 project.
Findent and BSD: macOS, openBSD and freeBSD
Long time ago, somebody checked for me if findent compiles on macOS. It did. Since I have no Apple, I didn't check again and assumed that it still would be ok.
Alas, not so. A former colleague of mine was kind enough to compile findent on his Mac, and there were some issues:
- clang (C++ on the Mac) is not entirely compatible with g++. That was easy to repair.
- BSD-sed(1) is not compatible with gnu-sed in at least two points, which were not difficult to deal with: sed is only used to remove '\r' characters. So I replaced 'sed' with 'tr'.
- MacOS uses bash 3.x, while Ubuntu uses bash 4.x, which allows the 'declare -A' command to create associative arrays. This issue was resolved by using awk in stead of bash.
- getopt(1) in BSD is different from gnu-getopt: for example, it does not facilitate long flags. Getopt is used in wfindent. The problem is resolved as follows: If wfindent cannot find a suitable getopt (gnu-getopt returns 4 with the flag -T) it returns with a message.
- MacOS and openBSD users can easily install a working getopt, for example via (macOS:) Homebrew or (openBSD:) 'pkg_add gnugetopt' or (freeBSD:) 'pkg install getopt'. If the environment variable WFINDENT_GETOPT is set to the location of a gnu-getopt, it will use that.
In a nutshell: since version 3.1.2 findent should compile and run ok in macOS, openBSD and freeBSD.
In the mean time, I succeeded to install a virtual MacBook Pro, so I can check myself if findent compiles and run in MacOS.
Other Fortran indenters
Good free Fortran indenters are 'not easy to find'. Searching the Internet, I was not able to find good working free available Fortran indenters, with the exception of f90ppr. F90ppr is written in Fortran and does a nice and quick job, but lacks a number of the requirements above.
A more recent development is fprettify, written in Python. IMHO the software is not mature enough for use in serious projects.
Other Fortran relabelers
These are difficult to find. Floppy, written in Fortran is an example, but works only for old style Fortran.