From:	GALAXY::GLEEVE       13-MAY-1992 10:10:47.45
To:	ARISIA::EVERHART
CC:	
Subj:	Fortran line renumberer in awk

From:	MERC::"uunet!CRVAX.SRI.COM!RELAY-INFO-VAX" 13-MAY-1992 09:45:22.47
To:	info-vax@kl.sri.com
CC:	
Subj:	Fortran line renumbering program

Some time ago someone requested a line renumbering program for Fortran
statement labels.  At the time I posted a more or less incomplete
awk program to do this (it was sufficient for the Fortran programs *I*
write).

Shortly thereafter, Myron Calhoun (mac@cis.ksu.edu) expressed some
interest in it, and we electronically put our heads together to produce
a program that might just work on most Fortran programs.

The original poster has indicated that he had received several
solutions to his problem.  However, we've got this program laying
around now, begging to be posted, so, at the cost of thousands of
dollars, here it is:

--Radey Shouman

-----------cut-here-----------cut-here-------cut-here--------------------
#!/bin/nawk -f
# Subject: AWK program to Renumber FORTRAN statements wanted
# 
# DISCLAIMER:  This program has not been tested, it was written as a lark,
#              there is no warranty, express or implied that it will do
#              anything in particular.
# 
# CLAIMER:  I wouldn't post it if I didn't think it would (usually) work.
# Specifically, it looks for statement labels and renumbers them 10, 20, 30, ...
# in each routine encountered, then it goes back and substitutes for the old
# label numbers in ASSIGN, CALL, computed GOTO, DO, arithmetic IF, GOTO, 
# PRINT, PUNCH, READ, and WRITE statments (including FMT=, END= and ERR= 
# in READ and WRITE statements).
# Although GOTO may also be written GO TO, all other keywords are assumed to
# consist of contiguous non-blank characters.  All white space is assumed to
# be spaces, tabs may cause undesired consequences.  
#
# KEYWORD OPTIONS:  You can specify several awk-style keyword arguments
# on the command line.  (If you want to use gawk, you probably need to use
# the -v option.)  
# The options are: (default)
#     start:(10)        the first label to use in each subprogram.
#     inc:(start)       the increment between labels
#     fstart:     a separate starting label for format statements.
#     finc: (inc) the increment between format labels.
# If you don't specify the format parameters, format statements will
# be numbered in the same sequence as all other statements. No checking
# is done to see whether format labels and other labels collide,  (the 
# compiler will do that :-) so be sure to set a generous format_start
#
# BUGS:  This program understands fortran keywords, not fortran.  If you 
# give variables the same names as keywords, or don't separate words by
# blanks or punctuation, or leave blanks in the middle of words, then 
# you're not interested in readable programs anyway.
#    The READ/WRITE statement regexp may not work correctly if you have
# a multiply nested parenthesized expression, with commas, as you unit
# number:  WRITE(ARR(1,2),33) is ok, WRITE(FUN(ARR(1,2)), 33) is not.
# If you really use such baroque unit number specifications, you could
# fix this program too.
#    There are probably other valid fortran statements that this program
# doesn't handle correctly.

BEGIN \
{ if ( start == "" )		# The first statement label number to use.
    start = 10;
  num = start;

  if ( inc == "" )		# The increment between statement labels.
    inc = 10;		

  if ( fstart != "" )	# Start, increment for format statement labels.
    { if (finc == "")
	finc = inc;
      fnum = fstart;
    }
	  
  line = 0;
  
# Regexps to match key words in either case.
  _assign_ = " *[Aa][Ss][Ss][Ii][Gg][Nn]";
  _do_     = " *[Dd][Oo]";
  _call_   = " *[Cc][Aa][Ll][Ll]";
  _end_    = " *[Ee][Nn][Dd]";
  _err_    = " *[Ee][Rr][Rr]";
  _fmt_    = " *[Ff][Mm][Tt]";
  _goto_   = " *[Gg][Oo] *[Tt][Oo]";
  _if_     = " *[Ii][Ff]";
  _print_  = " *[Pp][Rr][Ii][Nn][Tt]";
  _punch_  = " *[Pp][Uu][Nn][Cc][Hh]";
  _read_   = " *[Rr][Ee][Aa][Dd]";
  _write_  = " *[Ww][Rr][Ii][Tt][Ee]";

  keywords = "\("_assign_"\|"_do_"\|"_call_"\|"_goto_"\|"_if_"\|"_print_\
    "\|"_punch_"\|"_read_"\|"_write_"\)"

# Regexps matching statements that use line numbers.

# "do" type patterns have one  or zero line numbers immediately after the 
# keyword:
  patterns["do"] = "^[^Cc*!].*[ )]\(" _assign_ "\|" _do_ "\|" _goto_ \
     "\|" _print_ "\|" _punch_ "\) *[0-9]+";

# "cgoto" type patterns have a comma separated list of line numbers after
# the either "IF (expression)", or "GOTO (" .
  patterns["cgoto"] = "^[^Cc*!].*[ )]\(" _if_ ".*[)]\|" _goto_ \
	" *\([a-zA-Z][^(]*\)? *[(]\)"   "\( *[a-zA-Z][^,()]*,\)* *[0-9]+";
  pat_next["cgoto"] = "^ *,\( *[a-zA-Z][^,()]*,\)* *[0-9]+";
    
# "read" type patterns have a READ or WRITE key word, followed by a unit
# number and exactly one comma, then zero or one line numbers, possibly 
# followed by END, ERR, or FMT keywords.
  patterns["read"] = "^[^Cc*!].*[ )]\(" _read_ "\|" _write_ "\)"\
    " *[(]\([^=,]*\([(][^()][)][^=,]\)*, *[0-9]+\?\)";
  pat_next["read"] = "\(" _end_ "\|" _err_ "\|" _fmt_ "\) *= *[0-9]+";

# "call" type patterns have a CALL key word, followed by a right parenthesis,
# followed by zero or one "*line number", possibly followed by comma separated
# stuff including "*line number"'s.
  patterns["call"] = "^[^Cc*!].*[ )]"_call_ "[^(]*[(] *\([*][0-9]\)*";
  pat_next["call"] = ", *[*][0-9]+";

# Enough "x"'s to replace any quoted sting we hope to find.
	xstr="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

# Enough " "'s to replace any embedded comment we might find.
	bstr="                                                              "\
              "                                  ";
}

/\t/ {  print FNR ":  WARNING:  tabs found\n" | "cat 1>&2"; 
	line++;
	routine[line] = "C#####WARNING:  tabs found";
      }

		# Append continuation lines, watch for embedded comments.
/^[^Cc*!]....[^ ]/ \
 { while ( match(routine[line],"^[Cc*!]") != 0 )
     { routine[line - 1] = routine[line - 1] "\n" routine[line];
       line--;
     }
  routine[line] = routine[line] "\n" $0;
  next;
 }

/^ *[0-9]+/ \
{ oldnum = $1 + 0;              # Find statement labels, replace them with
                                # renumbered ones, old-new correspondence
                                # is array line_nums.
  if ( fstart != "" && $0 ~ /^[ 0-9]*[Ff][Oo][Rr][Mm][Aa][Tt]/ )
    { line_nums[oldnum] = fnum;
      fnum = fnum + finc;
    }
  else
    { line_nums[oldnum] = num;   
      num = num + inc;
    }
  $0 = sprintf("%5d ",line_nums[oldnum]) substr($0,7)
 }

{ line++;                       # Save all input lines from current routine
  routine[line] = $0;           # in array routine.
 }

/^[ \t0-9]*[Ee][Nn][Dd][ \t\n]*$/  \
{ for (i = 1; i <= line; i++)    # The end of the current routine.
    { test_line = routine[i];       

				# Ignore comments, lines without any numbers,
				# and lines without any of the keywords 
				# associated with statement labels.
      if (match(routine[i],"^[Cc*!]") != 0 ||\
             match(routine[i],"[0-9]") ==0 || match(routine[i],keywords) == 0 )
	  continue;
				# Make continuation characters match whitespace

				# Turn quoted strings into x's.
      while ( match(test_line, "'[^']*'\|\"[^\"]*\"") != 0 )
	test_line = substr(test_line,1,RSTART - 1) substr(xstr,1,RLENGTH) \
	    substr(test_line, RSTART + RLENGTH);

				# Turn embedded comments into blanks.
      while ( match(test_line, "\n") != 0 )
	{ if ( match(test_line, "!.*\n"))
            test_line = substr(test_line,1,RSTART - 1) \
             substr(bstr,1,RLENGTH - 1) substr(test_line,RSTART + RLENGTH - 1);
	  if ( match(test_line, "\n[Cc*!].*\n") != 0 )
	    test_line = substr(test_line,1,RSTART - 1) substr(bstr,1,RLENGTH - 1) \
	      substr(test_line, RSTART + RLENGTH - 1);	    
          else
            sub("\n[^Cc*!]....[^ ]","       ",test_line);  # (That's 7 spaces)
	}

				# Look for statement-number labels being used.
      for ( j in patterns )
        { if ( match(test_line, patterns[j]) == 0)
              continue;
          rlength = RLENGTH;

          if ( match(substr(test_line, 1, rlength), "[0-9]+$") != 0 )
             { oldnum = substr(test_line, RSTART) + 0;
               rlength++;
               newnum = line_nums[oldnum];

                if ( newnum == "" )
                  { routine[i-1] = routine[i-1] \
		      "\nC##### WARNING: statement label " oldnum\
			" not matched"
		    print routine[i-1] "\n" FNR ": " routine[i] | "cat 1>&2";
                    newnum = oldnum;
                   }

                routine[i] = substr(routine[i], 1, RSTART - 1) newnum \
                       substr(routine[i], rlength);
                offset = length(newnum) - length(oldnum);
              }
	  else
	    offset = 0;

          if ( pat_next[j] == "") break;
          while ( match(substr(test_line, rlength), pat_next[j]) != 0 )
             { rlength = rlength - 2 + RSTART + RLENGTH;
               if (match(substr(test_line, 1, rlength), "[0-9]+$") == 0)
		 continue;
               oldnum = substr(test_line, RSTART, rlength - RSTART + 1);
               newnum = line_nums[oldnum];

                if ( newnum == "" )
                  { routine[i-1] = routine[i-1] \
		      "\nC##### WARNING: statement label " oldnum\
			" not matched"
		    print routine[i-1] "\n" FNR ": " routine[i] | "cat 1>&2";
                    newnum = oldnum;
                   }

               routine[i] = substr(routine[i], 1, RSTART - 1 + offset) newnum \
                      substr(routine[i], rlength + 1 + offset);
	       offset = offset + length(newnum) - length(oldnum);
	       rlength++;	# Move on to a possible next pattern.
             }

             break;             # Match at most one pattern.
         }

    }

  for (i = 1; i <= line; i++) 
      print routine[i];		# Print this routine out.

  num = start;
  if ( fstart != "")
    fnum = fstart;
  line = 0;

  for (i in line_nums)
    delete line_nums[i];
 }

END    { for (i = 1; i <= line; i++)   # Print anything after the last "END".
           print routine[i]
       }

-- 
Radey Shouman					rshouman@chpc.utexas.edu

