Download and installation instructions for version 7.50 of plusFORT, on Windows, Mac and Linux, can be found at:
https://fortran.uk/currentversion.
Your licence comprises the following 3 lines of text:
fortran-lang.discourse.group - Windows, Mac or Linux
Valid until 31st March 2022
ZDDAAZCJJBJDHMDKFP
This licence is valid until the end of March 2022. To request an extension beyond that date, please get in touch via our website. We ask that you seriously consider purchasing a licence if you use the software meaningfully, particularly in a commercial of government environment. Please let us know how you get on.
Brief Overview of plusFORT
There is much in plusFORT that cannot be found anywhere else. More detail, including video tutorials and an online manual, can be found at www.fortran.uk. plusFORT supports all standards up to Fortran 2003, as well as VAX Fortran, and many other extensions. It can be used from the command line, or using PFFE, our intuitive GUI front-end.
Code Modernization
Source code restructuring and spaghetti unscrambler
Source form conversion
Automatically generates declarations to allow use of IMPLICIT NONE
Translates declarations to and from Fortran 2003 form
Automatically adds INTENT to argument declarations
Over 100 configurable options to customize code transformation
Code Instrumentation
Automatically insert probes to analyse CPU usage
Dynamic Analysis. Inserts probes to identify illegal use of
uninitialized data (much more thorough than compile-time checks).
Coverage Analysis. Inserts probes to identify execution hotspots, and
code-blocks which remain untested through a series of runs.
Static Analysis
HTML reports (with extensive crosslinks between reports) showing usage
and problems relating to
subprograms
subprogram arguments
modules and module variables
COMMON blocks and variables
PARAMETERs
local variables
Hyperlinked HTML call tree
Modularization
Analysis of legacy Fortran code in preparation for conversion to
modular structure.
Internalization - shows subprograms and COMMON blocks that
could be internal (i.e. CONTAINed within another executable
subprogram)
Cluster Analysis - identifies subprograms and data structures
which could be combined in a module.
HyperKWIC
A complete refresh of the traditional KWIC (KeyWord In Context)
report.
Every occurrence of every user symbol is hyperlinked, to allow users
to skip back and forth between reports and source code.
This is great, thanks. I have a question regarding what would be considered fair use. There is a lot of public domain FORTRAN code at Netlib and elsewhere that needs updating with something like plusFORT. If someone used plusFORT on such code and put it on GitHub, would you consider that fair use?
A situation where you are paid to modernize a company’s proprietary Fortran code would be different and should entail a purchase of plusFORT.
Yes that would be fine. I’d prefer to issue a separate gratis non-commercial licence specifically for that usage. If you (or anyone else) would like to get in touch via our website, I’ll send you one.
I’ve barely scratched the surface, but initial results with packages like ODEPACK are very encouraging. That’s a very generous offer. I think modernizing some popular OS packages would benefit the Fortran community and give you some useful feedback and visibility in return.
One thing on my wish list that I do not see (yet) is an ability to instrument the codes to generate capture-and-playback unit tests but a lot of the parts seem to be there. A lot of the old codes do not have very complete test suites, which makes additional manual refactoring riskier. Did I miss that or is that possibly a future feature?
Thanks for your feedback! I’m unsure about the answer to your question - does Coverage Analysis come close to what you need? It can accumulate data across a series of test runs with different inputs, to identify linear code blocks that are not exercised.
SPAG is able to insert calls to probes at various points in your code (see below). The probe routines are written in Fortran, and the source is supplied, so it’s quite possible for a user to adapt them to particular needs. As an example, when Y2K was an issue, we adapted the routines to spot things that looked like dates.
Probes can be inserted at various points to monitor control flow
At the start of execution, and before a controlled termination
on entering or leaving a routine
on entering a linear block of code (with no transfers)
The Dynamic analysis facility also inserts probes which change or monitor the values of variables:
when variables become formally undefined (e.g. unsaved local variables on entry to a subprogram)
when variables are used (to check they are defined)
/opt$ sudo tar -xzvf plusfort_x64Linux_750.tgz
tar: This does not look like a tar archive
tar: Skipping to next header
tar: Exiting with failure status due to previous errors
Looks like you downloaded it twice. Check whether the one you try to untar is actually the correct one (via the SHA256 checksum which is given on the download page).
You probably have two downloaded files, plusfort_x64Linux_750.tgz and plusfort_x64Linux_750.tgz.1. The first of these is probably a leftover from an earlier failed or aborted download. Delete it, rename the second by removing the “.1” suffix, and then proceed to extract/install.
Thanks for the advice. I think at my first attempt downloading through Firefox I hit the mangling issue, hence I used wget but didn’t notice it went to a new file. The checksum of this one (found with sha256sum plusfort_x64Linux_750.tgz) matched the one on the download page. The archive was now extracted correctly.
The SPAG tool looks interesting. I tried it for a few of my source files, both modern and dusty ones, but I was not really convinced by the result yet.
For example taking toml-f/table.f90 at main · toml-f/toml-f · GitHub results in an invalid Fortran source file in the SPAGged directory. Also, I find the all caps style and the mixed format output somewhat irritating. But maybe I’m using it wrong.
Output of running SPAG on table.f90
!*==tomlf_type_table.f90 processed by SPAG 7.50RH at 19:51 on 20 Jan 2022
! This file is part of toml-f.
! SPDX-Identifier: Apache-2.0 OR MIT
!
! Licensed under either of Apache License, Version 2.0 or MIT license
! at your option; you may not use this file except in compliance with
! the License.
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!> Implementation of the TOML table data type.
!>
!> Every TOML document contains at least one (root) table which holds key-value
!> pairs, arrays and other tables.
MODULE TOMLF_TYPE_TABLE
USE TOMLF_CONSTANTS , ONLY:TFC
USE TOMLF_ERROR , ONLY:TOML_STAT
USE TOMLF_TYPE_VALUE , ONLY:toml_value , TOML_VISITOR , TOML_KEY
USE TOMLF_STRUCTURE , ONLY:TOML_STRUCTURE , NEW_STRUCTURE
IMPLICIT NONE
!*--TOMLF_TYPE_TABLE25
PRIVATE
PUBLIC :: TOML_TABLE , new_table , new
!> TOML table
TYPE , EXTENDS :: (toml_value) :: TOML_TABLE
!> Table was implictly created
LOGICAL :: IMPLICIT = .FALSE.
!> Is an inline table and is therefore non-extendable
LOGICAL :: INLINE = .FALSE.
!> Storage unit for TOML values of this table
CONTAINS
IMPLICIT NONE
!*--AA000144
!> Get the TOML value associated with the respective key
!> Get list of all keys in this table
!> Check if key is already present in this table instance
!> Append value to table (checks automatically for key)
!> Delete TOML value at a given key
!> Release allocation hold by TOML table
END TYPE
!> Create standard constructor
INTERFACE TOML_TABLE
MODULE PROCEDURE :: NEW_TABLE_FUNC
END INTERFACE
!> Overloaded constructor for TOML values
INTERFACE NEW
MODULE PROCEDURE :: NEW_table
END INTERFACE
CONTAINS
!> Constructor to create a new TOML table and allocate the internal storage
SUBROUTINE NEW_TABLE(Self)
IMPLICIT NONE
!*--NEW_TABLE79
!> Instance of the TOML table
TYPE (TOML_TABLE) , INTENT(OUT) :: Self
CALL NEW_STRUCTURE(Self%LIST)
END SUBROUTINE NEW_TABLE
!> Default constructor for TOML table type
FUNCTION NEW_TABLE_FUNC() RESULT(SELF)
IMPLICIT NONE
!*--NEW_TABLE_FUNC92
!> Instance of the TOML table
TYPE (TOML_TABLE) :: SELF
CALL NEW_TABLE(SELF)
END FUNCTION NEW_TABLE_FUNC
!> Get the TOML value associated with the respective key
SUBROUTINE GET(SELF,Key,Ptr)
IMPLICIT NONE
!*--GET105
!> Instance of the TOML table
!> Key to the TOML value
CHARACTER(KIND=tfc,LEN=*) , INTENT(IN) :: Key
!> Pointer to the TOML value
CALL SELF%LIST%FIND(Key,Ptr)
END SUBROUTINE GET
!> Get list of all keys in this table
SUBROUTINE GET_KEYS(SELF,List)
IMPLICIT NONE
!*--GET_KEYS122
!> Instance of the TOML table
!> List of all keys
TYPE (TOML_KEY) , ALLOCATABLE , INTENT(OUT) :: List(:)
CALL SELF%List%GET_KEYS(List)
END SUBROUTINE GET_KEYS
!> Check if a key is present in the table
FUNCTION HAS_KEY(SELF,Key) RESULT(FOUND)
IMPLICIT NONE
!*--HAS_KEY137
!> Instance of the TOML table
!> Key to the TOML value
CHARACTER(KIND=tfc,LEN=*) , INTENT(IN) :: Key
!> TOML value is present in table
LOGICAL :: FOUND
CALL SELF%LIST%FIND(Key,ptr)
FOUND = ASSOCIATED(ptr)
END FUNCTION HAS_KEY
!> Push back a TOML value to the table
SUBROUTINE PUSH_BACK(SELF,Val,Stat)
IMPLICIT NONE
!*--PUSH_BACK158
!> Instance of the TOML table
!> TOML value to append to table
!> Status of operation
INTEGER , INTENT(OUT) :: Stat
IF ( .NOT.ALLOCATED(Val) ) THEN
Stat = toml_stat%FATAL
RETURN
ENDIF
IF ( .NOT.ALLOCATED(Val%KEY) ) THEN
Stat = toml_stat%FATAL
RETURN
ENDIF
IF ( SELF%HAS_KEY(Val%KEY) ) THEN
Stat = toml_stat%DUPLICATE_KEY
RETURN
ENDIF
CALL SELF%LIST%PUSH_BACK(Val)
Stat = toml_stat%SUCCESS
END SUBROUTINE PUSH_BACK
!> Delete TOML value at a given key
SUBROUTINE DELETE(SELF,Key)
IMPLICIT NONE
!*--DELETE192
!> Instance of the TOML table
!> Key to the TOML value
CHARACTER(KIND=tfc,LEN=*) , INTENT(IN) :: Key
CALL SELF%LIST%DELETE(Key)
END SUBROUTINE DELETE
!> Deconstructor to cleanup allocations (optional)
SUBROUTINE DESTROY(SELF)
IMPLICIT NONE
!*--DESTROY207
!> Instance of the TOML table
IF ( ALLOCATED(SELF%KEY) ) DEALLOCATE (SELF%KEY)
IF ( ALLOCATED(SELF%LIST) ) THEN
CALL SELF%LIST%DESTROY
DEALLOCATE (SELF%LIST)
ENDIF
END SUBROUTINE DESTROY
END PROGRAM AA0001
My apologies - the original post was misleading. SPAG supports Fortran 95, but not Fortran 2003 - my unaccountable brain-fog. As a result, SPAG produces quite a few errors when processing your code.
16 occurrences of Statement not recognised
4 occurrences of Local variable used but not set
8 occurrences of MODULE not found in PFMODULE.KEY
The use of capitals is very configurable, and I think it likely you could configure it to your taste. The default uses case to distinguish different types of object - for example Fortran keywords and user names
Case of Variables etc
(-1=leave,0=lower
n=n caps then lower
9=upper)
100=0 case of local variables
101=3 case of COMMON variables
102=1 case of dummy arguments
103=9 case of PARAMETERS
104=9 case of other symbols
Case of Other Elements
106=1 case of logical operators
(0=lower,1=upper)
107=1 case of FORTRAN keywords
(0=lower,1=upper)
108=0 case of character strings
(0=leave,-1=lower,1=upper)
109=0 case of comments
(0=leave,-1=lower,1=upper)
I tried Plusfort, but had no time to properly check out the options it gave me. I recently came across a very nasty bug in an Fortran program I maintain. The bug is specific to fixed-form, and something that in my opinion should not be allowed, but it passes and compiles with GFortran just fine. Then I thought: would plusfort discover such a bug?
I therefore created a small reproducer code with the bug:
SUBROUTINE multiplication(c, a, b)
IMPLICIT NONE
REAL, INTENT(out) :: c
REAL, INTENT(in) :: a, b
c = a *
& * b
END SUBROUTINE multiplication
In real life, the code was significantly more complicated than this, of course. This code compiles with Gfortran 9.3. If I rewrite to free-form it does not compile any more.
When I run the above mentioned code through spag it does not complain significantly about anything and re-write the code into:
!*==multiplication.f90 processed by SPAG 7.50RH at 08:56 on 4 Feb 2022
SUBROUTINE MULTIPLICATION(C,A,B)
IMPLICIT NONE
!*--MULTIPLICATION4
REAL , INTENT(OUT) :: C
REAL , INTENT(IN) :: A , B
C = A**B
END SUBROUTINE MULTIPLICATION
In my opinion there should have been a warning on this…
Welcome to the forum, and thanks for the example. At least translating to free source form clarifies what the code does, which the programmer must compare to his intention.
@hakostra, consider the following version of your fixed form code:
SUBROUTINE multiplication(c, a, b)
IMPLICIT NONE
REAL, INTENT(out) :: c
REAL, INTENT(in) :: a, b
c = a *
* * b
END SUBROUTINE multiplication
It differs only in that the continuation character (in col.6) is ‘*’ instead of ‘&’. What do you expect to be the semantics of this version? What do you expect a Fortran compiler or SPAG to do with it?