Bug(s) in Ratfor90, Ratfor(?)

I suspect that few members of fortran-lang.discourse.group use or are even aware of Ratfor.

As of now, Ratfor is a language whose usefulness is over, but now and then we come across packages of substantial size that are coded in Ratfor, and we have to decide what to do with them.

There are three main versions of Ratfor, in increasing order of readability/usability of the output Fortran code (the first two converters are C programs, the third is a Perl script):

  1. The original Ratfor from Bell Labs, which converts source code in Ratfor to Fortran 66. The output is not suitable for human maintenance or inspection.
  2. The newer Ratfor77, which outputs Fortran 77 source code. Of marginal interest in the year 2022.
  3. Ratfor90, which outputs Fortran 90 source code.

Ratfor90 appears from its description to be capable of producing F90 output that may be easier to understand and maintain than the F77 or F66 output of the older Ratfor converters. Unfortunately, after trying it on the Quantreg package (for use with R), I found that the conversion produces seriously buggy code. This is an unfortunate state of affairs, since some of the Quantreg source code, which is in R, cannot be converted to F90 except by hand conversion, and the conversion of the same Quantreg Ratfor to F77 or F66 source produces code that few would want to read.

Here is a demonstration of the bug in Ratfor90. The input test program in Ratfor:

subroutine sakj(x,p,nx)
integer nx,i
double precision x(nx),p(nx)
double precision sum0,qrange

	sum0=0d0
	for(i=1;i<nx;i=i+1) {
		sum0=sum0+p(i)
		if(sum0<.25) next
		else { qrange=x(i);break }
		}
        
	sum0=1d0
	for(i=nx;i>0;i=i-1) {
		sum0=sum0-p(i)
		if(sum0>.75) next
		else { qrange=x(i)-qrange;break }
		}
     print '(2E12.4)',sum0,qrange
return
end

program tsakj
integer nx
parameter (nx = 11)
double precision x(nx),p(nx)
integer i
for(i=1; i<=nx; i=i+1)
   x(i) = (i-1)*0.1d0
for(i=1; i < nx; i= i+1)
   p(i) = i*0.1d0-0.05d0
p(nx) = 0.99d0
call sakj(x,p,nx)
end

and a manual translation to F90:

! manual translation of ts.r to f90
!
subroutine sakj(x,p,nx)
integer nx,i
double precision x(nx),p(nx)
double precision sum0,qrange

	sum0=0d0
	do i=1,nx-1
		sum0=sum0+p(i)
		if(sum0 < 0.25d0) cycle
		qrange=x(i)
        exit
	end do
	sum0=1d0
	do i = nx,1,-1
		sum0=sum0-p(i)
		if(sum0 > 0.75d0) cycle
		qrange=x(i)-qrange
        exit
	 end do
     print '(2E12.4)',sum0,qrange
return
end

program tsakj
integer nx
parameter (nx = 11)
double precision x(nx),p(nx)
integer i
do i = 1, nx
   x(i) = (i-1)*0.1d0
end do
do i = 1, nx-1
   p(i) = i*0.1d0-0.05d0
end do
p(nx) = 0.99d0
call sakj(x,p,nx)
end program

After converting using Ratfor77 and running the resulting Fortran program, the output is the same as from the manually generated F90 program:

  0.1000E-01  0.8000E+00

The output after converting using Ratfor90:

  0.1000E-01  0.1000E+01

Here is where the bug occurs. For the Ratfor code section

        sum0=0d0
        for(i=1;i<nx;i=i+1) {
                sum0=sum0+p(i)
                if(sum0<.25) next
                else { qrange=x(i);break }
                }

Ratfor90 outputs the following F90 code:

sum0=0d0
i=1
do
if(.not. (i<nx)) then
  exit
end if
  sum0=sum0+p(i)
  if (sum0<.25) then
    cycle
  else
    qrange=x(i)
    exit
  end if
  i=i+1
end do

The serious error is that the statement i = i+1 is never reached, and p(1) and x(1) are used again and again where p(i) and x(i) should have been used. This makes Ratfor90 unusable for Ratfor code that contains FOR loops.

I was not directly involved, but when I was inquiring what happened to a large amount of RATFOR code
at an organization I was previously associated with I was told that it had been processed into Fortran 77 (with ratfor77?, not sure) and been used as-is until relatively recently, when it was run through the commercial spag(1) utility from plusfort and maintenance resumed. They were happy with the results. plusfort might provide access for an Open Source project if you contact them. Fixiing ratfor90 might be relatively easy, though(?).

1 Like

Yes, using SPAG or VAST90 works for most of the Ratfor-converted F66 source files, but in a few cases the logic is too complicated for that to succeed.

One file in particular, rqbr.r from Quantreg, resulted in an F90 file that the compiler rejected because there were multiple GOTO statement from outside DO…END DO and IF…THEN…ENDIF constructs to labels inside the constructs. It is these longer source files with convoluted logic that often result in failure of the project as a whole, even if all the other sources are converted satisfactorily.

Ratfor90 is a Perl script of about 2,000 lines. I don’t know Perl, and its slogan, “There is more than one way to do it”, is discouraging for me.

The problem is that Ratfor90 is blindly converting next to cycle. That doesn’t work inside a for loop because the iteration-expression (i.e. the 3rd expression in the for) doesn’t get executed after the cycle. The next should get converted to a goto to the code for the
iteration-expression.

BTW, this conversion also means that if, for example, you were to call a function named ieee_next_after it would get converted to ieee_cycle_after.

It is certainly possible to have a well-written 2000-line Perl program, but I don’t think this is an example of one.

1 Like

Did you reach that conclusion by looking at the Fortran 90 output of the conversion, or at the relevant portion of the Perl conversion script itself? If the latter, and you know Perl well enough, can you suggest a fix for the issue?

Thanks for your explanation. But, given that Fortran does not have reserved keywords, translating next to cycle without regard for context would be asking for trouble, would it not? In fact, both next and cycle could be variable names in Fortran programs.

By looking at the script. But I didn’t see any obvious way to fix it.

If you have to use Ratfor90, I suggest rewriting to eliminate any occurrences of next inside a for loop.

Worse than that, next is replaced by cycle even if it is part of a name (see my ieee_next_after example above).

OK, thanks.