.\" @(#)m2 6.1 (Berkeley) 4/29/86
attempts to retain the merits of Fortran
(universality, portability, efficiency)
while hiding the worst Fortran inadequacies.
Fortran except for two aspects.
since control flow is central to any program,
regardless of the specific application,
is to conceal this part of Fortran from the user,
by providing decent control flow structures.
These structures are sufficient and comfortable
for structured programming in the narrow sense of programming without
Second, since the preprocessor must examine an entire program
to translate the control structure,
it is possible at the same time to clean up many of the
``cosmetic'' deficiencies of Fortran,
and thus provide a language which is easier
and more pleasant to read and write.
Beyond these two aspects _ control flow and cosmetics _
does nothing about the host of other weaknesses of Fortran.
Although it would be straightforward to extend
they are not needed by everyone,
the preprocessor would be harder to implement.
Throughout, the design principle which has determined
doesn't know any Fortran.
Any language feature which would require that
really understand Fortran has been omitted.
We will return to this point in the section
Even within the confines of control flow and cosmetics,
we have attempted to be selective
in what features to provide.
The intent has been to provide a small set of the most useful
rather than to throw in everything that has ever been thought useful
The rest of this section contains an informal description
The control flow aspects will be
quite familiar to readers used to languages like
Algol, PL/I, Pascal, etc.,
and the cosmetic changes are equally straightforward.
showing what the language looks like.
Fortran provides no way to group statements together,
short of making them into a subroutine.
The standard construction
``if a condition is true,
do this group of things,''
{ call error("x>100"); err = 1;
cannot be written directly in Fortran.
a programmer is forced to translate this relatively
clear thought into murky Fortran,
by stating the negative condition
and branching around the group of statements:
When the program doesn't work,
or when it must be modified,
this must be translated back into
a clearer form before one can be sure what it does.
eliminates this error-prone and confusing back-and-forth translation;
the way the computation is written in
A group of statements can be treated as a unit
by enclosing them in the braces { and }.
This is true throughout the language:
there can be several enclosed in braces.
(Braces seem clearer and less obtrusive than
already have Fortran meanings.)
contribute to the readability of code,
and thus to its understandability.
The character ``>'' is clearer than
translates it appropriately,
along with several other similar shorthands.
Although many Fortran compilers permit character strings in quotes
converts it into the right number of
computers count better than people do.
statements may appear anywhere on a line,
and several may appear on one line
if they are separated by semicolons.
The example above could also be written as
In this case, no semicolon is needed at the end of each line because
assumes there is one statement per line
if the statement that follows the
No continuation need be indicated
because the statement is clearly not finished on the first line.
continues lines when it seems obvious that they are not yet done.
(The continuation convention is discussed in detail later.)
Although a free-form language permits wide latitude in formatting styles,
it is wise to pick one that is readable, then stick to it.
In particular, proper indentation is vital,
to make the logical structure of the program obvious to the reader.
statement to handle the construction
``if a condition is true,
{ sw = 0; write(6, 1) a, b }
{ sw = 1; write(6, 1) b, a }
This writes out the smaller of
then the larger, and sets
The Fortran equivalent of this code is circuitous indeed:
This is a mechanical translation;
as they do for many similar situations.
But all translations suffer from the same problem:
since they are translations,
they are less clear and understandable than code
that is not a translation.
To understand the Fortran version,
one must scan the entire program to make
sure that no other statement branches
before one knows that indeed this is an
there is no question about how one gets to the parts of the statement.
which can be read, understood, and ignored if not relevant.
The program says what it means.
As before, if the statement following an
is a single statement, no braces are needed:
if (\fIlegal Fortran condition\fP)
anything that can legally go into a Fortran Logical
does not check this clause,
since it does not know enough Fortran
to know what is permitted.
is any Ratfor or Fortran statement, or any collection of them
Since the statement that follows an
can be any Ratfor statement, this leads immediately
to the possibility of another
As a useful example, consider this problem:
Here the statement after the first
Logically it is just a single statement,
although it is rather complicated.
This code says what it means.
Any version written in straight Fortran
will necessarily be indirect
because Fortran does not let you say what you mean.
And as always, clever shortcuts may turn out
to be too clever to understand a year from now.
is one way to write a multi-way branch in Ratfor.
provides a way to specify the choice of exactly one of several alternatives.
statement which does the same job
in certain special cases;
in more general situations, we have to make do
The tests are laid out in sequence, and each one
is followed by the code associated with it.
of decisions until one is found that is satisfied.
The code associated with this condition is executed,
and then the entire structure is finished.
part handles the ``default'' case,
where none of the other conditions apply.
If there is no default action, this final
There is one thing to notice about complicated structures
This is a genuine ambiguity in Ratfor, as it is in many other programming
The ambiguity is resolved in Ratfor
(as elsewhere) by saying that in such cases the
goes with the closest previous
as we have indicated by the indentation.
It is a wise practice to resolve such cases by explicit braces,
just to make your intent clear.
In the case above, we would write
which does not change the meaning, but leaves
no doubt in the reader's mind.
If we want the other association, we
provides a clean way to express multi-way branches
which branch on the value of some integer-valued expression.
\f3switch (\fIexpression\|\f3) {
case \fIexpr2, expr3\f3 :
list of comma-separated integer expressions.
is compared against the case expressions
at which time the statements following that
the statements with it are done;
as soon as some block of statements is executed,
(Readers familiar with C[4] should beware that this
behavior is not the same as the C
except that it uses no statement number.
The statement number, after all, serves only to mark the end
and this can be done just as easily with braces.
do \fIlegal\(hyFortran\(hyDO\(hytext\fP
has to be something that can legally go into a Fortran
Thus if a local version of Fortran allows
(which is not currently permitted in
part will often be enclosed in braces, but
a single statement need not have braces around it.
This code sets an array to zero:
Slightly more complicated,
sets the upper triangle of
to \-1, the diagonal to zero, and the lower triangle to +1.
(The operator == is ``equals'', that is, ``.EQ.''.)
In each case, the statement that follows the
statement, even though complicated,
and thus needs no braces.
provides a statement for leaving a loop early,
and one for beginning the next iteration.
causes an immediate exit from the
in effect it is a branch to the statement
is a branch to the bottom of the loop,
so it causes the next iteration to be done.
For example, this code skips over negative values in an array:
\fIprocess positive element\fP
also work in the other Ratfor looping constructions
that we will talk about in the next few sections.
can be followed by an integer to indicate breaking or iterating
that level of enclosing loop; thus
exits from two levels of enclosing loops,
iterates the second enclosing loop.
not likely to be much used
because they lead to code that is hard to understand
and somewhat risky to change.)
One of the problems with the Fortran
is that it generally insists upon being done once,
regardless of its limits.
this will typically be done once with
even though common sense would suggest that perhaps it shouldn't be.
can easily be preceded by a test
but this has to be a conscious act,
and is often overlooked by programmers.
A more serious problem with the
is that it encourages that a program be written
in terms of an arithmetic progression
with small positive steps,
even though that may not be the best way to write it.
If code has to be contorted to fit the requirements
it is that much harder to write and understand.
To overcome these difficulties,
``while some condition is true,
repeat this group of statements''.
no preconceptions about why one is looping.
For example, this routine to compute sin(x)
combines two termination criteria.
.ta .3i .6i .9i 1.2i 1.5i 1.8i
# returns sin(x) to accuracy e, by
# sin(x) = x - x**3/3! + x**5/5! - ...
while (abs(term)>e & i<100) {
term = -term * x**2 / float(i*(i-1))
if the routine is entered with
that is, no attempt will be made to compute
and thus a potential underflow is avoided.
Since the test is made at the top of a
a special case disappears _
the code works at one of its boundaries.
making sure the routine stops after
some maximum number of iterations.)
As an aside, a sharp character ``#'' in a line
marks the beginning of a comment;
the rest of the line is comment.
Comments and code can co-exist on the same line _
one can make marginal remarks,
which is not possible with Fortran's ``C in column 1'' convention.
Blank lines are also permitted anywhere
(they are not in Fortran);
they should be used to emphasize the natural divisions
while (\fIlegal Fortran condition\fP)
is something that can go into
which may be multiple statements in braces.
encourages a style of coding not normally
practiced by Fortran programmers.
is a function which returns the next input character
both as a function value and in its argument.
Then a loop to find the first non-blank character is just
while (nextch(ich) == iblank)
A semicolon by itself is a null statement,
which is necessary here to mark the end of the
if it were not present, the
would control the next statement.
contains the first non-blank.
Of course the same code can be written in Fortran as
100 if (nextch(ich) .eq. iblank) goto 100
but many Fortran programmers (and a few compilers) believe this line is illegal.
The language at one's disposal
strongly influences how one thinks about a problem.
is another Ratfor loop, which
attempts to carry the separation of
loop-body from reason-for-looping
statement allows explicit initialization
and increment steps as part of the statement.
for (i = 1; i <= n; i = i + 1) ...
The initialization and increment of
making it easier to see at a glance
versions have the advantage that they will be done zero times
is less than 1; this is not true of the
The loop of the sine routine in the previous section
for (i=3; abs(term) > e & i < 100;
term = -term * x**2 / float(i*(i-1))
for ( \fIinit\fP ; \fIcondition\fP ; \fIincrement\fP )
is any single Fortran statement, which gets done once
is any single Fortran statement,
which gets done at the end of each pass through the loop,
is again anything that is legal in a logical
is treated as always true,
statement is particularly
backward loops, chaining along lists,
loops that might be done zero times,
and similar things which are hard to express with a
to find the last non-blank character on a card:
for (i = 80; i > 0; i = i - 1)
The code scans the columns from 80 through to 1.
If a non-blank is found, the loop
This code is rather nasty to write with a regular Fortran
since the loop must go forward,
and we must explicitly set up proper conditions
when we fall out of the loop.
(Forgetting this is a common error.)
.ta .3i .6i .9i 1.2i 1.5i 1.8i
IF (CARD(I) .NE. BLANK) GO TO 11
The version that uses the
handles the termination condition properly for free;
zero when we fall out of the
need not be an arithmetic progression;
the following program walks along a list
(stored in an integer array
until a zero pointer is found,
adding up elements from a parallel array of values:
for (i = first; i > 0; i = ptr(i))
Notice that the code works correctly if the list is empty.
Again, placing the test at the top of a loop
instead of the bottom eliminates a potential boundary error.
The ``repeat-until'' statement
In spite of the dire warnings,
there are times when one really needs a loop that tests at the bottom
This service is provided by the
until (\fIlegal Fortran condition\fP)
then the condition is evaluated.
If it is true, the loop is exited;
if it is false, another pass is made.
part is optional, so a bare
is the cleanest way to specify an infinite loop.
Of course such a loop must ultimately be broken by some
transfer of control such as
or an implicit stop such as running out of input with
As a matter of observed fact[8], the
less used than the other looping constructions;
in particular, it is typically outnumbered ten to one by
Be cautious about using it, for loops that test only at the
bottom often don't handle null cases well.
and to the increment step of a
The standard Fortran mechanism for returning a value from a function uses the name of the function as a variable which can be assigned to;
the last value stored in it
is the function value upon return.
For example, here is a routine
which returns 1 if two arrays are identical,
The array ends are marked by the special value \-1.
.ta .3i .6i .9i 1.2i 1.5i 1.8i
# equal _ compare str1 to str2;
# return 1 if equal, 0 if not
integer function equal(str1, str2)
integer str1(100), str2(100)
for (i = 1; str1(i) == str2(i); i = i + 1)
In many languages (e.g., PL/I)
return (\fIexpression\fP)
to return a value from a function.
Since this is often clearer, Ratfor provides such a
{ F = expression; return }
.ta .3i .6i .9i 1.2i 1.5i 1.8i
# equal _ compare str1 to str2;
# return 1 if equal, 0 if not
integer function equal(str1, str2)
integer str1(100), str2(100)
for (i = 1; str1(i) == str2(i); i = i + 1)
If there is no parenthesized expression after
the visual appearance of a language
on how easy it is to read and understand
Accordingly, Ratfor provides a number of cosmetic facilities
which may be used to make programs more readable.
Statements can be placed anywhere on a line;
long statements are continued automatically,
as are long conditions in
Multiple statements may appear on one line,
if they are separated by semicolons.
No semicolon is needed at the end of a line,
can make some reasonable guess about whether the statement
Lines ending with any of the characters
are assumed to be continued on the next line.
Underscores are discarded wherever they occur;
all others remain as part of the statement.
Any statement that begins with an all-numeric field is
assumed to be a Fortran label,
and placed in columns 1-5 upon output.
write(6, 100); 100 format("hello")
Text enclosed in matching single or double quotes
but is otherwise unaltered
(except for formatting _ it may get split across card boundaries
during the reformatting process).
Within quoted strings, the backslash `\e' serves as an escape character:
the next character is taken literally.
This provides a way to get quotes (and of course the backslash itself) into
is a string containing a backslash and an apostrophe.
the standard convention of doubled quotes,
but it is easier to use and more general.)
Any line that begins with the character `%'
is left absolutely unaltered
except for stripping off the `%'
and moving the line one position to the left.
This is useful for inserting control cards,
and other things that should not be transmogrified
(like an existing Fortran program).
Use `%' only for ordinary statements,
not for the condition parts of
etc., or the output may come out in an unexpected place.
The following character translations are made,
except within single or double quotes
or on a line beginning with a `%'.
In addition, the following translations are provided
for input devices with restricted character sets.
Any string of alphanumeric characters can be defined as a name;
thereafter, whenever that name occurs in the input
(delimited by non-alphanumerics)
it is replaced by the rest of the definition line.
(Comments and trailing white spaces are stripped off).
A defined name can be arbitrarily long,
and must begin with a letter.
is typically used to create symbolic parameters:
dimension a(ROWS), b(ROWS, COLS)
if (i > ROWS \(or j > COLS) ...
Alternately, definitions may be written as
In this case, the defining text is everything after the comma up to the balancing
this allows multi-line definitions.
It is generally a wise practice to use symbolic parameters
for most constants, to help make clear the function of what
would otherwise be mysterious numbers.
As an example, here is the routine
again, this time with symbolic constants.
.ta .3i .6i .9i 1.2i 1.5i 1.8i
# equal _ compare str1 to str2;
# return YES if equal, NO if not
integer function equal(str1, str2)
integer str1(ARB), str2(ARB)
for (i = 1; str1(i) == str2(i);
The standard usage is to place
that file whenever a copy is needed:
This ensures that all copies of the
Pitfalls, Botches, Blemishes and other Failings
Ratfor catches certain syntax errors, such as missing braces,
and most errors involving missing parentheses in statements.
Beyond that, since Ratfor knows no Fortran,
any errors you make will be reported by the Fortran compiler,
so you will from time to time have to relate a Fortran diagnostic back
etc., as variable names will typically wreak havoc.
Don't leave spaces in keywords.
convention is not recognized anywhere by Ratfor;