PROGRAM nr_elf90 ! Attempts to converts Numerical Recipes Fortran 90 code to make it compatible ! with the ELF90 compiler. The principal remaining problems after conversion ! are: ! 1. SAVE statements will need to be replaced, perhaps by adding extra ! arguments to routines. ! 2. Some functions have more than one returned result. These may have ! to be changed into subroutines. ! 3. Some explicit declarations will have to be changed to assumed array ! dimensions, e.g. change DIMENSION(4,4) to DIMENSION(:,:). ! The conversions performed are as follows: ! Tabs are replaced with spaces ! Functions are converted from e.g. REAL FUNCTION f(x) to ! FUNCTION f(x) RESULT(fn_val) ! All occurrences of the function name within the program unit are changed ! to fn_val. ! A RETURN statement is added at the end of each function or subroutine. ! If there are multiple USE statements on a line separated by semi-colons, ! the line is broken. ! In MODULE's, an IMPLICIT NONE statement is added after the last USE, if ! there is not one already. ! ELF90 does not support SAVE statements; if they are found, a warning is given. ! An unnecessary test for lines containing only an END statement has been ! added to prevent the program hanging with non-NR code. ! ELF90 requires a double colon (::) after TYPE; this is added. ! INTENT(INOUT) is changed to INTENT(IN OUT). ! If a function or subroutine has no arguments, `()' is added. This is also ! done for CALL statements. ! A RETURN is inserted before CONTAINS in subroutines and functions. ! WHERE statements are converted into WHERE constructs. ! Programmer: Alan Miller (Alan.Miller @ mel.dms.csiro.au) ! WWW-page: http://www.mel.dms.csiro.au/~alan ! Fax: (+61) 3-9545-8080 ! Latest revision - 14 November 1996 ! This is public domain software. It may be used and distributed freely AS IS. ! If you make changes to the code, add comments giving your name and indicating ! what changes have been made. ! No responsibility for errors in the converted code is accepted by the author. IMPLICIT NONE TYPE :: code CHARACTER (LEN=140) :: text TYPE (code), POINTER :: next END TYPE code CHARACTER (LEN=40) :: infile, outfile CHARACTER (LEN=140) :: temp_text CHARACTER (LEN=4) :: unit_type CHARACTER (LEN=32) :: unit_name CHARACTER (LEN=9) :: delimiters = ' =+-*/,()' CHARACTER (LEN=8) :: date CHARACTER (LEN=10) :: time CHARACTER (LEN=1) :: tab TYPE (code), POINTER :: head, current, tail, new_line, last_line INTEGER :: pos, last, iostatus, count, len_name, length, depth LOGICAL :: SAVE_msg, imp_none_needed, ret_needed tab = CHAR(9) ! Open the Fortran 90 file ! Open a file for the output with extension `elf' DO WRITE(*, *)'Enter name of Fortran source file: ' READ(*, '(a)') infile IF (INDEX(infile, '.') == 0) THEN last = LEN_TRIM(infile) infile(last+1:last+4) = '.f90' END IF OPEN(8, file=infile, status='old') pos = INDEX(infile, '.') outfile = infile(1:pos) // 'elf' OPEN(9, file=outfile) ! Set up a linked list containing the lines of code NULLIFY(head, tail) ALLOCATE(head) tail => head READ(8, '(a)') head % text IF (head % text(1:1) == tab) head % text = head % text(2:) count = 1 DO ALLOCATE(current) READ(8, '(a)', IOSTAT=iostatus) current % text IF (iostatus /= 0) EXIT count = count + 1 DO pos = INDEX(current % text, tab) IF (pos > 0) THEN current % text = current % text(:pos-1) // ' ' // current % text(pos+1:) ELSE EXIT END IF END DO NULLIFY(current % next) tail % next => current tail => current END DO WRITE(*, *)'File: ', infile, ' No. of lines read =', count current => head SAVE_msg = .FALSE. DO ! Find first non-comment or blank line DO temp_text = ADJUSTL(current % text) IF (temp_text(1:1) /= '!' .AND. LEN_TRIM(temp_text) > 0) EXIT current => current % next IF (.NOT. ASSOCIATED(current)) EXIT END DO IF (.NOT. ASSOCIATED(current)) EXIT imp_none_needed = .FALSE. ! Find whether program unit is a MODULE, SUBROUTINE, INTERFACE or FUNCTION IF (INDEX(temp_text, 'SUBROUTINE') > 0) THEN unit_type = 'SRTN' ret_needed = .TRUE. ELSE IF (INDEX(temp_text, 'FUNCTION') > 0) THEN unit_type = 'FUNC' ret_needed = .TRUE. ELSE IF (INDEX(temp_text, 'INTERFACE') > 0) THEN unit_type = 'INTF' ret_needed = .FALSE. ELSE unit_type = 'MODL' imp_none_needed = .TRUE. ret_needed = .FALSE. END IF ! Get the name of the program unit pos = INDEX(temp_text, ' ') + 1 last = INDEX(temp_text, '(') - 1 IF (last < 0) last = LEN_TRIM(temp_text) unit_name = temp_text(pos:last) len_name = last + 1 - pos CALL convert(unit_type, unit_name, len_name) IF (.NOT. ASSOCIATED(current)) EXIT current => current % next IF (.NOT. ASSOCIATED(current)) EXIT END DO !-------------------------------------------------------------------------- ! Output the new file current => head WRITE(9, '(a)') current % text(1:LEN_TRIM(current % text)) current => current % next CALL DATE_AND_TIME(date, time) WRITE(9, '("! Code converted using NR_ELF90 by Alan Miller")') WRITE(9, '("! Date: ", a4, "-", a2, "-", a2, " Time: ", a2, ":", a2, & & ":", a2)') date(1:4), date(5:6), date(7:8), time(1:2), & time(3:4), time(5:6) DO IF (.NOT. ASSOCIATED(current)) EXIT WRITE(9, '(a)') current % text(1:LEN_TRIM(current % text)) current => current % next END DO CLOSE (8) CLOSE (9) END DO STOP !-------------------------------------------------------------------------- CONTAINS !-------------------------------------------------------------------------- RECURSIVE SUBROUTINE convert(unit_type, unit_name, len_name) IMPLICIT NONE CHARACTER (LEN=4), INTENT(IN) :: unit_type CHARACTER (LEN=32), INTENT(IN OUT) :: unit_name INTEGER, INTENT(IN) :: len_name ! Local variables CHARACTER (LEN=50) :: terminator CHARACTER (LEN=4) :: new_type CHARACTER (LEN=32) :: new_name INTEGER :: new_length, i, nbrackets ! If the program unit is a function, change its form to: ! FUNCTION unit_name(arguments) RESULT(fn_val) IF (unit_type == 'FUNC') THEN IF (INDEX(current % text, 'RESULT') == 0) THEN current % text = current % text(1:LEN_TRIM(current % text)) // & ' RESULT(fn_val)' ELSE unit_name = '***' END IF END IF ! Add () if a function or subroutine has no arguments IF (unit_type == 'FUNC' .OR. unit_type == 'SRTN') THEN IF (INDEX(current % text, '(') == 0) THEN length = LEN_TRIM(current % text) current % text(length+1:length+2) = '()' END IF END IF ! Form the terminator for this program unit IF (unit_type == 'INTF') THEN terminator = 'INTERFACE' ELSE pos = INDEX(current % text, 'RECURSIVE') IF (pos > 0) THEN temp_text = current % text(:pos-1) // current % text(pos+10:) ELSE temp_text = current % text END IF pos = INDEX(temp_text, '(') - 1 IF (pos <= 0) pos = LEN_TRIM(temp_text) terminator = ADJUSTL(temp_text(:pos)) END IF DO last_line => current current => current % next IF (current % text(1:1) == '!') CYCLE ! Skip comments IF (LEN_TRIM(current % text) == 0) CYCLE ! Skip blank lines IF (INDEX(current % text, 'END ') > 0) THEN ! End of program unit reached IF (INDEX(current % text, terminator) > 0) THEN ! Add RETURN at the end of functions or subroutines, except for CONTAINed ! functions or subroutines. IF (INDEX(last_line % text, ' END ') /= 0) THEN IF (INDEX(last_line % text, 'FUNCTION') /= 0 .OR. & INDEX(last_line % text, 'SUBROUTINE') /= 0) ret_needed = .FALSE. END IF IF (ret_needed) THEN ALLOCATE(new_line) last_line % next => new_line CALL calc_indent() new_line % text = temp_text(1:depth) // 'RETURN' new_line % next => current last_line => new_line END IF RETURN END IF END IF temp_text = ADJUSTL(current % text) ! Unnecessary test for a line IF (LEN_TRIM(temp_text) == 3) THEN ! containing only `END' IF (temp_text(1:3) == 'END' .OR. temp_text(1:3) == 'end' .OR. & temp_text(1:3) == 'End') THEN current % text = terminator ! Add RETURN at the end of functions or subroutines IF (INDEX(last_line % text, ' END ') /= 0) THEN IF (INDEX(last_line % text, 'FUNCTION') /= 0 .OR. & INDEX(last_line % text, 'SUBROUTINE') /= 0) ret_needed = .FALSE. END IF IF (ret_needed) THEN ALLOCATE(new_line) last_line % next => new_line CALL calc_indent() new_line % text = temp_text(1:depth) // 'RETURN' new_line % next => current last_line => new_line END IF RETURN END IF END IF IF (INDEX(current % text, 'END ') == 0) THEN pos = INDEX(current % text, 'FUNCTION') IF (pos > 0) THEN new_type = 'FUNC' ELSE pos = INDEX(current % text, 'SUBROUTINE') IF (pos > 0) THEN new_type = 'SRTN' ELSE pos = INDEX(current % text, 'INTERFACE') IF (pos > 0) new_type = 'INTF' END IF END IF IF (pos > 0) THEN ! Convert inner program unit last = pos + INDEX(current % text(pos:), '(') - 2 pos = pos + INDEX(current % text(pos:), ' ') new_name = current % text(pos:last) new_length = last + 1 - pos IF (new_type == 'FUNC' .OR. new_type == 'SRTN') THEN imp_none_needed = .TRUE. IF (unit_type == 'INTF') THEN ret_needed = .FALSE. ELSE ret_needed = .TRUE. END IF ELSE imp_none_needed = .FALSE. ret_needed = .FALSE. END IF CALL convert(new_type, new_name, new_length) IF (unit_type == 'MODL') THEN ret_needed = .FALSE. ELSE ret_needed = .TRUE. END IF CYCLE END IF END IF IF (INDEX(current % text, 'USE ') > 0) THEN ! Look for semi-colon after USE ! If found, break the line pos = INDEX(current % text, ';') IF (pos > 0) THEN ALLOCATE(new_line) length = LEN_TRIM(current % text) CALL calc_indent() IF (depth > 0) THEN new_line % text = temp_text(1:depth) // ADJUSTL(current % text(pos+1:length)) ELSE new_line % text = ADJUSTL(current % text(pos+1:length)) END IF current % text(pos:) = ' ' new_line % next => current % next current % next => new_line END IF ELSE ! Insert IMPLICIT NONE IF (imp_none_needed) THEN IF (INDEX(current % text, 'IMPLICIT NONE') == 0) THEN IF (INDEX(last_line % text, '&') == 0) THEN ALLOCATE(new_line) CALL calc_indent() new_line % text = temp_text(1:depth) // 'IMPLICIT NONE' imp_none_needed = .FALSE. last_line % next => new_line new_line % next => current END IF ELSE imp_none_needed = .FALSE. END IF END IF END IF IF (unit_type == 'FUNC') THEN ! Change function name last = 1 DO length = LEN_TRIM(current % text) pos = INDEX(current % text(last:length), unit_name(:len_name)) IF (pos > 0) THEN pos = pos + last - 1 ! Check that there are delimiters ! at each end of name IF (pos == 1 .OR. SCAN(current % text(pos-1:pos-1), delimiters) > 0) THEN IF (SCAN(current % text(pos+len_name:pos+len_name), delimiters) > 0) THEN current % text = current % text(1:pos-1) // 'fn_val' // & current % text(pos+len_name:length) END IF END IF last = pos + len_name ELSE EXIT END IF END DO END IF ! Cannot handle SAVE statements ! Issue warning message pos = INDEX(current % text, 'SAVE') IF (pos > 0 .AND. .NOT. SAVE_msg) THEN ! Check that there are delimiters ! at each end of name IF (pos == 1 .OR. SCAN(current % text(pos-1:pos-1), delimiters) > 0) THEN IF (SCAN(current % text(pos+4:pos+4), delimiters) > 0) THEN WRITE(*, *) 'SAVE found in file:', infile WRITE(*, *) 'ELF90 does not allow SAVE statements' SAVE_msg = .TRUE. END IF END IF END IF temp_text = ADJUSTL(current % text) ! Insert :: after TYPE IF (temp_text(1:4) == 'TYPE') THEN IF (INDEX(temp_text, '::') == 0) THEN pos = INDEX(current % text, 'TYPE') current % text = current % text(:pos+3) // ' ::' // current % text(pos+4:) END IF END IF pos = INDEX(current % text, 'INOUT') ! Change INOUT to IN OUT IF (pos > 0) THEN current % text = current % text(:pos+1) // ' ' // current % text(pos+2:) END IF IF (INDEX(current % text, 'call ') /= 0) THEN ! Insert () if a subroutine IF (INDEX(current % text, '(') == 0) THEN ! has no arguments length = LEN_TRIM(current % text) current % text(length+1:length+2) = '()' END IF END IF ! Insert RETURN before CONTAINS IF (INDEX(current % text, 'CONTAINS') /= 0) THEN IF (unit_type == 'SRTN' .OR. unit_type == 'FUNC') THEN ALLOCATE(new_line) last_line % next => new_line CALL calc_indent() new_line % text = temp_text(1:depth) // 'RETURN' new_line % next => current last_line => new_line END IF END IF ! Convert WHERE statements to WHERE constructs pos = INDEX(current % text, 'where') IF (pos > 0) THEN ! Find closing bracket of length = LEN_TRIM(current % text) ! the condition nbrackets = 0 DO i = pos+5, length IF (current % text(i:i) == '(') THEN nbrackets = nbrackets + 1 ELSE IF (current % text(i:i) == ')') THEN nbrackets = nbrackets - 1 IF (nbrackets == 0) EXIT END IF END DO IF (i < length) THEN ! If nothing follows, it is ALLOCATE(new_line) ! a WHERE construct CALL calc_indent() new_line % text = temp_text(:depth+3) // current % text(i+1:length) current % text(i+1:) = ' ' new_line % next => current % next current % next => new_line current => new_line ! Skip lines ending with '&' DO pos = LEN_TRIM(current % text) IF (current % text(pos:pos) == '&') THEN current => current % next ELSE EXIT END IF END DO ALLOCATE(new_line) new_line % text = temp_text(:depth) // 'END where' new_line % next => current % next current % next => new_line current => new_line END IF END IF END DO RETURN END SUBROUTINE convert SUBROUTINE calc_indent() IMPLICIT NONE depth = 1 DO IF (current % text(depth:depth) == ' ') THEN depth = depth + 1 ELSE EXIT END IF END DO depth = depth - 1 temp_text = ' ' RETURN END SUBROUTINE calc_indent END PROGRAM nr_elf90