1 RATFOR 4/4/78 RATFOR RATFOR PRIMER Ratfor is a preprocessor for Fortran. Its primary purpose is to encourage readable and well-structured code while taking advantage of the universality, portability, and efficiency of Fortran. This is done by providing the control structures not available in bare Fortran, and by improving the "cosmetics" of the language. Ratfor allows for all the features of normal Fortran, plus makes available these control structures: "if"-"else" "while", "for", and "repeat"-"until" for looping "break" and "next" for controlling loop exits statement grouping with braces The cosmetic aspects of Ratfor have been designed to make it concise and reasonably pleasing to the eye: free form input unobtrusive comment convention translation of >, <=, etc. into .GT., .LE., etc. string data type quoted character strings "define" statement for symbolic constants conditional preprocessing "include" statement for including source files Ratfor is implemented as a preprocessor which translates the above features into Fortran, which can then be fed into almost any Fortran compiler. Each of the Ratfor features will now be discussed in more detail. In the following, a "statement" is any legal statement in Fortran: assignment, declaration, subroutine call, I/O, etc., or any of the Ratfor statements themselves. Any Fortran or Ratfor statement or group of these can be enclosed in braces ({}) or brackets ([]) -- to make it a compound statement, which is then equivalent to a single statement and usable anywhere a single statement can be used. -1- 1 RATFOR 4/4/78 RATFOR IF-ELSE Ratfor provides an "else" statement to handle the construction "if a condition is true, do this thing, otherwise do that thing". The syntax is if (legal Fortran condition) statement(s) else statement(s) where the else part is optional. The "legal Fortran condition" is anything that can legally go into a Fortran logical IF. The Ratfor statements may be one or more valid Ratfor or Fortran statements of any kind. If more than one statement is desired, the statements must be enclosed by braces. For example, if (a > b) { k = 1 call remark (...) } else if (a < b) { k = 2 call remark (...) } else return -2- 1 RATFOR 4/4/78 RATFOR WHILE Ratfor provides a while statement, which is simply a loop: "while some condition is true, repeat this group of statements". The syntax is while (legal Fortran condition) statement(s) As with the if, "legal Fortran condition" is something that can go into a Fortran logical IF. The condition is tested before execution of any of the Ratfor statements, so if the condition is not met, the loop will be executed zero times. Also, as with the IF, the Ratfor statements can be any valid Ratfor or Fortran constructs. If more than one statement is desired, the statements must be enclosed by braces. For example, while (getc(c) != EOF) { c = cnvt (c) call putc (c) } -3- 1 RATFOR 4/4/78 RATFOR FOR The "for" statement is similar to the "while" except that it allows explicit initialization and increment steps as part of the statement. The syntax is for (init; condition; increment) statement(s) where "init" is any single Fortran statement which gets done once before the loop begins. "Increment" is any single Fortran statement which gets done at the end of each pass through the loop, before the test. "Condition" is again anything that is legal in a logical IF. Any of init, condition, and increment may be omitted, although the semicolons must remain. A non-existent condition is treated as always true, so "for( ; ; ) " is an indefinite repeat. The "for" statement is particularly useful for backward loops, chaining along lists, loops that might be done zero times, and similar things which are hard to express with a DO statement. Here are two examples of "for" loops: for (i=1; getarg(i, file, MAXLINE) != EOF; i=i+1) { int = open (file, READ) while (getlin (line, int) != EOF) { for (j=80; j>0; j=j-1) call putc (line(j)) } call close (int) } The above code simply reads cards from a list of files, reverses the order of the characters, and writes the cards onto a standard output file. (The "!=" means . NE.) Groups of Fortran statements may be used in the "init" and "increment" clauses by using the usual grouping characters ({...} or [...]) and separating the statements with semicolons (;). For example: for ([i=1; j=1]; buf(i) != EOS; [i=i+2; j=j+1]) out(j) = buf(i) copies every other character in buf into consecutive locations in out. -4- 1 RATFOR 4/4/78 RATFOR REPEAT-UNTIL The "repeat-until" statements allow for repetition of a group of statements until a specified condition is met. The syntax is: repeat statement(s) until condition The "until" is optional. Once again, if more than one Ratfor statement is desired, the statements must be enclosed by brackets. If the "until" part is omitted, the result is an infinite loop which must be broken with a "break" or "next" statement (see below). An example of a repeat-until loop is: repeat { call putc (BLANK) col = col + 1 } until (tabpos(col,tabs) == YES) -5- 1 RATFOR 4/4/78 RATFOR BREAK and NEXT Ratfor provides statements for leaving a loop early and for beginning the next iteration. "Break" causes an immediate exit from whatever loop it is contained in (which may be a "while", "for", or "repeat"). Control resumes with the next statement after the loop. Only one loop is terminated by a "break", even if the "break" is contained inside several nested loops. For example: repeat { if (getc(c) == EOF) break ... } "Next" is a branch to the bottom of the loop, so it causes the next iteration to be done. "Next" goes to the condition part of a "while" or "until", to the top of an infinite "repeat" loop, and to the reinitialize part of a "for". For example: for (i=1; i<10; i=i+1) { if (array(i) == BLANK) next ... } -6- 1 RATFOR 4/4/78 RATFOR STATEMENT GROUPING AND NULL STATEMENTS Ratfor allows a group of statements to be treated as a unit by enclosing them in braces -- { and }. This is true throughout the language: wherever a single Ratfor statement can be used, there could also be several enclosed in braces. For example: if (x > 100) { call error (...) err = 1 return } If braces are not valid characters in the local operating system, the characters "[" and "]" may be used instead of "{" and "}" respectively. Ratfor also allows for null statements, most useful after "for" and "while" statements. A semicolon alone indicates a null statement. For instance, while (getlin(line, int) != EOF) ; would read lines from a file until the end-of-file was reached and for (i=1; line(i) == BLANK; i=i+1) ; positions after leading blanks in a line. -7- 1 RATFOR 4/4/78 RATFOR FREE-FORM INPUT Statements may be placed anywhere on a line and several may appear on one line if they are separated by semicolons. No semicolon is needed at the end of each line because Ratfor assumes there is one statement per line unless told otherwise. Ratfor will, however, continue lines when it seems obvious that they are not yet done. Any statement that begins with an all-numeric field is assumed to be a Fortran label and is placed in columns 1-5 upon output. Statements may be passed through the Ratfor compiler unaltered by inserting a percent sign (%) as the first character on the line. The percent will be removed, the rest of the line shifted one position to the left, and the line sent out without any changes. This is a convenient way to pass regular Fortran or assembly code through the ratfor compiler. -8- 1 RATFOR 4/4/78 RATFOR COMMENTS A sharp character "#" in a line marks the beginning of a comment and the rest of the line is considered to be that comment. Comments and code can co-exist on the same line. For example, function dummy (x) # I made up this function to show some comments dummy = x #I am simply returning the parameter return end -9- 1 RATFOR 4/4/78 RATFOR CHARACTER TRANSLATION Sometimes the characters >, <=, etc. are easier to read in Fortran condition statements than the standard Fortran . EQ., . LT., etc.. Ratfor allows either convention. If the special characters are used, they are translated in the following manner: == .EQ. != ^= ~= .NE. < .LT. > .GT. <= .LE. >= .GE. | .OR. & .AND. For example, for (i=1; i<= 5; i=i+1) ... if (j != 100) ... -10- 1 RATFOR 4/4/78 RATFOR STRING DATA TYPE All character arrays in Ratfor are sequences of ASCII characters, stored right-adjusted, one per array element, with the string terminated with an EOS marker. An automatic way to initialize string characters arrays is provided. The syntax is: string name "characters" or string name(n) "characters" Ratfor will define name to be a character (or, more likely, integer) array long enough to accomodate the ASCII codes for the given character string, one per element. The last word of name is initialized to EOS. If a size is given, name is declared to be an integer array of size 'n'. If several string statements appear consecutively, the generated declarations for the array will precede the data statements that initialize them. For example, the declarations: string errmsg "error" string done "bye" would be converted by ratfor into the Fortran: integer error(6) integer done(4) data error(1), error(2), error(3), error(4), error(5), error(6) /LETE, LETR, LETR, LETO, LETR, EOS/ data done(1), done(2), done(3), done(4) /LETD, LETO, LETN, LETE, EOS/ -11- 1 RATFOR 4/4/78 RATFOR QUOTED CHARACTER STRINGS Text enclosed in matching double or single quotes is converted to nH... format, but is otherwise unaltered. For instance, call remark ("Error detected") would translate to call remark (14hError detected) and data string /"Can't find answer"/ would become data string /17hCan't find answer/ If the local operating system does not support both upper and lower case hollerith strings, an escape mechanism is generally provided to allow the user to indicate case. Some operating systems are not capable of finding the end of a fortran hollerith string. In this case it may be necessary for the user to mark the end of her quoted string with a specific character, such as a period. -12- 1 RATFOR 4/4/78 RATFOR DEFINE 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. The syntax is: define(name, replacement string) which define "name" as a macro which will be replaced with "replacement string" when encountered in the source files. As a simple example: define(ROW,10) define(COLUMN,25) dimension array (ROW, COLUMN) and define(EOF,-1) if (getlin(line, fd) == EOF) .... Definitions may be included anywhere in the code, as long as they appear before the defined name occurs. The names of macro may contain letters, digits, and underline characters, but must start with a letter. Upper and lower cases ARE significant (thus EOF is not the same as eof). Any occurrences of the strings '$n' in the replacement text, where 1 <= n <= 9, will be replaced with the nth argument when the macro is actually invoked. For example: define(bump, $1 = $1 + 1) will cause the source line bump(i) to be expanded into i = i + 1 In addition to define, four other built-in macros are provided: arith(x,op,y) performs the "integer" arithmetic specified by op (+,-,*,/) on the two numeric operands and returns the result as its replacement. -13- 1 RATFOR 4/4/78 RATFOR incr(x) converts the string x to a number, adds one to it, and returns the value as its replacement (as a character string). ifelse(a,b,c,d) compares a and b as character strings; if they are the same, c is pushed back onto the input, else d is pushed back. substr(s,m,n) produces the substring of s which starts at position m (with origin one), of length n. If n is omitted or too big, the rest of the string is used, while if m is out of range the result is a null string. -14- 1 RATFOR 4/4/78 RATFOR CONDITIONAL PREPROCESSING Ratfor source code may be conditionally preprocessed, dependent upon the definition (or lack thereof) of a symbol. The syntax is ifdef(symbols) ifnotdef(symbol) . . . . . . elsedef elsedef . . . . . . enddef enddef Conditionals may be nested to some maximum level (usually 10). An example of their use might be an output routine which forces the output of a characters from a string to uppercase, depending upon the definition of a symbol DO_UPPER: for (i = 1; buf(i) != EOS; i = i + 1) { ifdef (DO_UPPER) call putc(cupper(buf(i)) elsedef call putc(buf(i)) enddef } -15- 1 RATFOR 4/4/78 RATFOR INCLUDE Files may be inserted into the input stream via the "include" command. The statement include filename or include "filename" inserts the file found on input file "filename" into the Ratfor input in place of the include statement. This is especially useful in inserting common blocks. For example, function exampl (x) include comblk exampl = x + z return end might translate into function exampl (x) common /comblk/ q, r, z exampl = x + z return end -16- 1 RATFOR 4/4/78 RATFOR IMPLEMENTATION Ratfor was originally written in C, a high-level language, on the Unix operating system. Our version is written in Ratfor itself, originally brought up by a bootstrap written in Fortran. Ratfor generates code by reading input files and translating any Ratfor keywords into standard Fortran. Thus, if the first token (word) on a source line is not a keyword (like "for", "while", etc.) the entire statement is simply copied to the output with appropriate character translation and formatting. Ratfor knows very little Fortran and thus does not handle any Fortran error detection. Errors in Ratfor keyword syntax are generally noted by a message to the user's terminal along with an indication of the source line number which caused the problem. CONCLUSIONS Ratfor demonstrates that with modest effort Fortran-based programmers can increase their productivity by using a language that provides them with the control structures and cosmetic features essential for structured programming design. Debugging and subsequent revision times are much faster than the equivalent efforts in Fortran, mainly because the code can be easily read. Thus it becomes easier to write code that is readable, reliable, and even esthetically pleasing, as well as being portable to other environments. -17- 1 RATFOR 4/4/78 RATFOR EXAMPLE The following is a sample Ratfor tool designed to show some of the commonly-used Ratfor commands. The routine reads through a list of files, counting the lines as it goes. # This is an example of a routine written in Ratfor # Symbols such as EOF, ERR, MAXLINE, character and filedes are # automatically defined (i.e. a file containing them is included) # by the preprocessor ## count - counts lines in files DRIVER(count) include comblk # this file contains a common block which # contains a variable "linect" character file(FILENAMESIZE), line(MAXLINE) integer i filedes fd integer getarg, open, getlin string total "total lines: " call query ("usage: count file.") linect = 0 #loop through the list of files for (i=1; getarg(i, file, FILENAMESIZE) != EOF; i=i+1) { fd = open (file, READ) # open (attach) the file if (fd == ERR) # file could not be located call cant (file) while (getlin(line, fd) != EOF) # read and count lines linect = linect + 1 call close (fd) # close (unattach) the file } call putlin(total, STDOUT) call putint (linect, 5, STDOUT) call putch (NEWLINE, STDOUT) DRETURN end -18- 1 RATFOR 4/4/78 RATFOR SEE ALSO 1) Kernighan, Brian W., "Ratfor--a Preprocessor for a Rational Fortran". _ + S_ + o_ + f_ + t_ + w_ + a_ + r_ + e _ + - _ + P_ + r_ + a_ + c_ + t_ + i_ + c_ + e _ + a_ + n_ + d _ + E_ + x_ + p_ + e_ + r_ + i_ + e_ + n_ + c_ + e_ + , Vol. 5, 4 (Oct-Dec 75), pp. 395-406. 2) Kernighan, Brian W. and P. J. Plauger, "Software Tools". Addison-Wesley Publishing Company, Reading, Mass., 1976. 3) The ratfor user document 4) The Unix command "rc" in the Unix Manual (RC(I)) -19- 1