RATFOR 7/15/80 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", "do", 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. quoted character strings "define" statement for symbolic constants "include" statement for including source files "return value " statement in functions format specified in read, write, encode, and decode statements 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. Please note that although the examples are given in lower case, upper case may be used as well for writing Ratfor. 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- RATFOR 7/15/80 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- RATFOR 7/15/80 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) ] Note that upper case is perfectly acceptable to Ratfor, and that brackets ([]) may be used instead of braces ({}). -3- RATFOR 7/15/80 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 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.) -4- RATFOR 7/15/80 DO The "do" statement is like the Fortran do-loop. The syntax is: do statement(s) If more than one Ratfor statement is desired, they must be enclosed in brackets. For example: do i = 1,10 { do j = 1,10 x(i,j) = 0.0 x(i,i) = 1.0 } -5- RATFOR 7/15/80 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) -6- RATFOR 7/15/80 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", "do", or "repeat"). Control resumes with the next statement after the loop. If "break" is followed by a number, then that many enclosing loops are exited, otherwise the loop in which the "break" appears is exited. For example: repeat { if (getc(c) == EOF) break ... } "Next" is a branch to the bottom of a 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, to the increment part of a "for", and to the next iteration of a "do". If a number is specified after the "next", then control is given to the condition part of the loop which is that many nested loops out from the "next", otherwise control is given to the condition part of the enclosing loop. For example: for (i=1; i<10; i=i+1) { if (array(i) == BLANK) next ... } -7- RATFOR 7/15/80 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, or if you wish to use upper case only, 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. -8- RATFOR 7/15/80 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, or if the line explicitly ends with an underline ("_"). Note that the underline is not included in the Fortran output of Ratfor. Do not attempt to use a continuation character in column 6 to continue a line. 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. Ratfor generates labels starting at 2000 and increasing by intervals of 10, so try to use labels under 2000 for your own uses. Statements may be passed through the Ratfor compiler unaltered by using the toggle character "%" on a line by itself before and after the lines desired to be literal. Thus, whenever a line beginning with "%" is encountered, all following lines are left absolutely unaltered until another line containing only a "%" is found. This is a convenient way to pass regular Fortran or assembly code through the Ratfor compiler. Be careful to indent Fortran code passed this way to column seven! -9- RATFOR 7/15/80 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 -10- RATFOR 7/15/80 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. ~= or ^= or != .NE. < .LT. > .GT. <= .LE. >= .GE. | or \ .OR. & .AND. ~ or ^ or ! .NOT. For example, for (i=1; i<= 5; i=i+1) ... if (j ~= 100) ... -11- RATFOR 7/15/80 QUOTED CHARACTER STRINGS Character strings may be enclosed in single or double quotes. To specify a quote character inside a string, repeat the character twice, e.g. if you want: he's back then say: 'he''s back' -12- RATFOR 7/15/80 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 (comments are stripped off). "Define" could be used to make these symbolic parameters: define(ROW,10) define(COLUMN,25) dimension array (ROW, COLUMN) and define(EOF,-1) if (getlin(line, int) == EOF) .... There cannot be a space between the word "define" and the left parenthesis. Definitions may be included anywhere in the code, as long as they appear before the defined name occurs. The name and its definition should not exceed 200 characters. Upper and lower cases ARE significant (thus EOF is not the same as eof), however one case or the other should be used. -13- RATFOR 7/15/80 INCLUDE Files may be inserted into the input stream via the "include" command. The statement 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 -14- RATFOR 7/15/80 RETURN The "return" statement may be used as usual in Ratfor. However, in a function subprogram the function value may be implicitly assigned before returning using the following syntax: return value Here "value" is the value of the function subprogram. For example: integer function index(char,string) byte string(80), char for (i = 1; i <= 80; i = i+1) if (string(i) == char) return i return 0 end -15- RATFOR 7/15/80 SPECIFYING FORMATS Format specifications may be included in read, write, encode, and decode statements by including the specfication, surrounded by parentheses, in the same position in the statement as the format statement number would normally appear. For example: write(5, ('$FILE? ')) read(5, (80a1), end=99) file Formats may be specified in the usual way by specifying a normal statement number instead of a parenthesized format specification. For example: read(5, 10, end=99) file 10 format(80a1) -16- RATFOR 7/15/80 USING RATFOR RAT4 expects one or more input files and one output file preceded by a ">". For example: RAT FILE1.RAT FILE2.RAT >FILE.FTN The above command would cause the files FILE1.RAT and FILE2.RAT to be compiled to FILE.FTN by Ratfor. Then the F4P compiler should be invoked, producing FILE.OBJ. The Ratfor compiler has one switch, the /SYMBOLS switch, which causes Ratfor to read a file with some standard DEFINES on it. Most people will not need this switch. If the Ratfor compiler detects an error in the Ratfor code, an error message will be printed to the terminal and to the . FTN file at the point where the error occurred. Many Fortran errors cannot be detected by Ratfor; when these occur, check the . LST file for the error message. It is usually not too hard to figure out where in the Ratfor source the error is, once it has been found in the Fortran source. -17- RATFOR 7/15/80 DIAGNOSTICS can't open symbols file The special symbols file containing general purpose ratfor definitions could not be opened. Possibly the user did not have access to the particular library the preprocessor expected to read. This is a diagnostic error only and processing will continue. can't open include File to be included was not a local file or file could not be opened due to lack of internal buffer space (At the present, only 6 levels of included files may be opened at a time.) definition too long The number of characters in the name to be defined exceeded Ratfor's internal array size (current maximum is 200 characters) format too long A format specification in a read, write, encode, or decode statement was too long (current maximum is 200 characters) illegal break Break did not occur inside a valid "while", "for", or "repeat" loop illegal else Else clause probably did not follow an "if" clause illegal next "Next" did not occur inside a valid "for", "while", or "repeat" loop illegal right brace A right brace was found without a matching left brace includes nested too deeply At the present, includes may only be nested 6 files deep invalid for clause The "for" clause did not contain a valid init, condition, and/or increment section missing comma in define Definitions must contain a comma between keyword and definition. missing left paren A parenthesis was expected, probably in an "if" statement, but not found missing parenthesis in condition A right parenthesis was expected, probably in an "if" statement, but not found missing quote A quoted string was not terminated by a quote missing right paren A right parenthesis was expected in a Fortran (as opposed to Ratfor) statement but not found non-alphanumeric name Definition keywords may contain only alphanumeric characters. stack overflow in parser Statements were nested at too deep a level. Current maximum is 100 statements nested at a time. token too long -18- RATFOR 7/15/80 A token (word) in the sourcecode was too long to fit into one of Ratfor's internal arrays. (Current maximum word size is 200 characters.) too many characters pushed back The sourcecode has illegally specified a Ratfor command, or has used a Ratfor keyword in an illegal manner, and the parser has attempted but failed to make sense out of it too many definitions Ratfor's internal arrays could not hold all the definitions. unbalanced parentheses Unbalanced parentheses detected in a Fortran (as opposed to Ratfor) statement unexpected brace or EOF A brace occurred after a Fortran (but not Ratfor) statement or an end-of-file was reached before the end of a statement unexpected EOF An end-of-file was reached before all braces had been accounted for. This is usually caused by unmatched braces somewhere deep in the sourcecode. warning possible label conflict This message is printed when the user has labeled a statement with a label in the 2000 and up range if the label is divisible by 10. Ratfor statements are assigned in this range and a user-defined one may conflict with a Ratfor-generated one. "file" cannot open Rat4 could not open an input file specified by the user. -19- RATFOR 7/15/80 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. This version of Ratfor originally came from Lawrence Berkely Labs; enhancements have since been made by William Wood at The Institute For Cancer Research, 7701 Burholme Ave., Philadelphia, PA. 19111. 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 does not know any 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. -20- RATFOR 7/15/80 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. The subroutines and functions which are used but not defined here are available in the Ratfor library, however you will probably have no need for them. # this is an example of a routine written in Ratfor # symbols such as EOF, ERR, MAXLINE, etc. are automatically # defined (i.e. a file containing them is included) by the # preprocessor if the "/SYMBOLS" switch is specified # at compile time. ## main calling routine call initr4 call count call endr4 end # count - counts lines in files subroutine count include comblk # this file contains a common block which # contains a variable "linect" character file(FILENAMESIZE), line(MAXLINE) integer i, getarg, int, open, getlin linect = 0 #loop through the list of files for (i=1; getarg(i, file, FILENAMESIZE) ~= EOF; i=i+1) { int = open (file, READ) # open (attach) the file if (int == ERR) { # file could not be located call remark ("can't open file") next } else # read lines from the file while (getlin(line, int) ~= EOF) linect = linect + 1 call close (int) # close (unattach) the file } return end -21- RATFOR 7/15/80 SEE ALSO 1) Kernighan, Brian W., "Ratfor--a Preprocessor for a Rational Fortran". Bell Laboratories publication. Also available from the UC Berkeley Computer Science library. 2) Kernighan, Brian W. and P. J. Plauger, "Software Tools". Addison-Wesley Publishing Company, Reading, Mass., 1976. 3) The rat4 user and design documents; the rc user document. 4) The Unix command "rc" in the Unix Manual (RC(I)) -22-