========== ratfor of chapter 9 ==========
define(MAXSTACK,10)
define(LEXDIGITS,-260)
define(LEXIF,-261)
define(LEXELSE,-262)
define(LEXWHILE,-263)
define(LEXBREAK,-264)
define(LEXNEXT,-265)
define(LEXDO,-266)
define(LEXOTHER,-267)
define(ALPHA,-100)
define(MAXTOK,10)
define(ALPHA,-100)
define(MAXTBL,500)
define(MAXPTR,50)
define(CALLSIZE,20)
define(ARGSIZE,100)
define(MAXDEF,200)
define(MAXTOK,200)
define(ARGFLAG,DOLLAR)
define(DEFTYPE,-10)
define(IFTYPE,-11)
define(INCTYPE,-12)
define(SUBTYPE,-13)
define(EVALSIZE,500)
define(BUFSIZE,500)
common /cdefio/ bp, buf(BUFSIZE)
   integer bp      # next available character; init = 0
   character buf   # pushed-back characters
common /cline/ linect
   integer linect   # line count on input file; init = 1
common /clook/ lastp, lastt, namptr(MAXPTR), table(MAXTBL)
   integer lastp      # last used in namptr; init = 0
   integer lastt      # last used in table; init = 0
   integer namptr      # name pointers
   character table      # actual text of names and defns
common /coutln/ outp, outbuf(MAXLINE)
   integer outp      # last position filled in outbuf; init = 0
   character outbuf      # output lines collected here
# alldig - return YES if str is all digits
   integer function alldig(str)
   character type
   character str(ARB)
   integer i
   alldig = NO
   if (str(1) == EOS)
      return
   for (i = 1; str(i) ~= EOS; i = i + 1)
      if (type(str(i)) ~= DIGIT)
         return
   alldig = YES
   return
   end
# balpar - copy balanced paren string
   subroutine balpar
   character gettok
   character t, token(MAXTOK)
   integer nlpar
   if (gettok(token, MAXTOK) ~= LPAREN) {
      call synerr("missing left paren.")
      return
      $@$
   call outstr(token)
   nlpar = 1
   repeat {
      t = gettok(token, MAXTOK)
      if (t==SEMICOL | t==LBRACE | t==RBRACE | t==EOF) {
         call pbstr(token)
         break
         $@$
      if (t == NEWLINE)      # delete newlines
         token(1) = EOS
      else if (t == LPAREN)
         nlpar = nlpar + 1
      else if (t == RPAREN)
         nlpar = nlpar - 1
      # else nothing special
      call outstr(token)
      $@$ until (nlpar <= 0)
   if (nlpar ~= 0)
      call synerr("missing parenthesis in condition.")
   return
   end
#block data
   block data
   include coutln
   include cline
   include cdefio
   data outp /0/
   data linect/1/
   data bp /0/
   end
# brknxt - generate code for break and next
   subroutine brknxt(sp, lextyp, labval, token)
   integer i, labval(MAXSTACK), lextyp(MAXSTACK), sp, token
   for (i = sp; i > 0; i = i - 1)
      if (lextyp(i) == LEXWHILE | lextyp(i) == LEXDO) {
         if (token == LEXBREAK)
            call outgo(labval(i)+1)
         else
            call outgo(labval(i))
         return
         $@$
   if (token == LEXBREAK)
      call synerr("illegal break.")
   else
      call synerr("illegal next.")
   return
   end
# docode - generate code for beginning of do
   subroutine docode(lab)
   integer labgen
   integer lab
#   string dostr "do"
   integer dostr(4)
   data dostr(1), dostr(2), dostr(3),
   dostr(4)/LETD, LETO, BLANK, EOS/
   call outtab
   call outstr(dostr)
   lab = labgen(2)
   call outnum(lab)
   call eatup
   call outdon
   return
   end
# dostat - generate code for end of do statement
   subroutine dostat(lab)
   integer lab
   call outcon(lab)
   call outcon(lab+1)
   return
   end
# eatup - process rest of statement; interpret continuations
   subroutine eatup
   character gettok
   character ptoken(MAXTOK), t, token(MAXTOK)
   integer nlpar
   nlpar = 0
   repeat {
      t = gettok(token, MAXTOK)
      if (t == SEMICOL | t == NEWLINE)
         break
      if (t == RBRACE) {
         call pbstr(token)
         break
         $@$
      if (t == LBRACE | t == EOF) {
         call synerr("unexpected brace or EOF.")
         call pbstr(token)
         break
         $@$
      if (t == COMMA) {
         if (gettok(ptoken, MAXTOK) ~= NEWLINE)
            call pbstr(ptoken)
         $@$
      else if (t == LPAREN)
         nlpar = nlpar + 1
      else if (t == RPAREN)
         nlpar = nlpar - 1
      call outstr(token)
      $@$ until (nlpar < 0)
   if (nlpar ~= 0)
      call synerr("unbalanced parentheses.")
   return
   end
# elseif - generate code for end of if before else
   subroutine elseif(lab)
   integer lab
   call outgo(lab+1)
   call outcon(lab)
   return
   end
# gettok - get token for Ratfor
   character function gettok(lexstr, toksiz)
   character ngetc, type
   integer i, toksiz
   character c, lexstr(toksiz)
   include cline
   while (ngetc(c) ~= EOF)
      if (c ~= BLANK & c ~= TAB)
         break
   call putbak(c)
   for (i = 1; i < toksiz-1; i = i + 1) {
      gettok = type(ngetc(lexstr(i)))
      if (gettok ~= LETTER & gettok ~= DIGIT)
         break
      $@$
   if (i >= toksiz-1)
      call synerr("token too long.")
   if (i > 1) {            # some alpha seen
      call putbak(lexstr(i))      # went one too far
      lexstr(i) = EOS
      gettok = ALPHA
      $@$
   else if (lexstr(1) == SQUOTE | lexstr(1) == DQUOTE) {
      for (i = 2; ngetc(lexstr(i)) ~= lexstr(1); i = i + 1)
         if (lexstr(i) == NEWLINE | i >= toksiz-1) {
            call synerr("missing quote.")
            lexstr(i) = lexstr(1)
            call putbak(NEWLINE)
            break
            $@$
      $@$
   else if (lexstr(1) == SHARP) {   # strip comments
      while (ngetc(lexstr(1)) ~= NEWLINE)
         ;
      gettok = NEWLINE
      $@$
   lexstr(i+1) = EOS
   if (lexstr(1) == NEWLINE)
      linect = linect + 1
   return
   end
# ifcode - generate initial code for if
   subroutine ifcode(lab)
   integer labgen
   integer lab
   lab = labgen(2)
   call ifgo(lab)
   return
   end
# ifgo - generate "if(.not.(...))goto lab"
   subroutine ifgo(lab)
   integer lab
#   string ifnot "if(.not."
   integer ifnot(9)
      data ifnot(1) /LETI/
      data ifnot(2) /LETF/
      data ifnot(3) /LPAREN/
      data ifnot(4) /PERIOD/
      data ifnot(5) /LETN/
      data ifnot(6) /LETO/
      data ifnot(7) /LETT/
      data ifnot(8) /PERIOD/
      data ifnot(9) /EOS/
   call outtab         # get to column 7
   call outstr(ifnot)      # " if(.not. "
   call balpar         # collect and output condition
   call outch(RPAREN)      # " ) "
   call outgo(lab)      # " goto lab "
   return
   end
# initkw - initialize keyword tables
   subroutine initkw
   integer sdo(3), sif(3), selse(5), swhile(6), sbreak(6), snext(5)
   integer vdo(2), vif(2), velse(2), vwhile(2), vbreak(2), vnext(2)
   data sdo(1),sdo(2),sdo(3) /LETD,LETO,EOS/
   data vdo(1),vdo(2) /LEXDO,EOS/
   data sif(1),sif(2),sif(3) /LETI,LETF,EOS/
   data vif(1),vif(2) /LEXIF,EOS/
   data selse(1),selse(2),selse(3),selse(4),selse(5) /LETE,
      LETL,LETS,LETE,EOS/
   data velse(1),velse(2) /LEXELSE,EOS/
   data swhile(1),swhile(2),swhile(3),swhile(4),swhile(5),
      swhile(6) /LETW,LETH,LETI,LETL,LETE,EOS/
   data vwhile(1),vwhile(2) /LEXWHILE,EOS/
   data sbreak(1),sbreak(2),sbreak(3),sbreak(4),sbreak(5),
      sbreak(6) /LETB,LETR,LETE,LETA,LETK,EOS/
   data vbreak(1),vbreak(2) /LEXBREAK,EOS/
   data snext(1),snext(2),snext(3),snext(4),snext(5) /LETN,
      LETE,LETX,LETT,EOS/
   data vnext(1),vnext(2) /LEXNEXT,EOS/
   call instal(sdo,vdo)
   call instal(sif,vif)
   call instal(selse,velse)
   call instal(swhile,vwhile)
   call instal(sbreak,vbreak)
   call instal(snext,vnext)
   return
   end
# labelc - output statement number
   subroutine labelc(lexstr)
   character lexstr(ARB)
   integer length
   if (length(lexstr) == 5)   # warn about 23xxx labels
      if (lexstr(1) == DIG2 & lexstr(2) == DIG3)
         call synerr("warning: possible label conflict.")
   call outstr(lexstr)
   call outtab
   return
   end
# labgen - generate  n  consecutive labels, return first one
   integer function labgen(n)
   integer label, n
   data label /23000/
   labgen = label
   label = label + n
   return
   end
# lex - return lexical type of token
   integer function lex(lexstr)
   character gettok
   character lexstr(MAXTOK)
   integer alldig, lookup
   integer ltype(2)
   while (gettok(lexstr, MAXTOK) == NEWLINE)
      ;
   lex = lexstr(1)
   if (lex==EOF | lex==SEMICOL | lex==LBRACE | lex==RBRACE)
      return
   if (alldig(lexstr) == YES)
      lex = LEXDIGITS
   else if (lookup(lexstr, ltype) == YES)
      lex = ltype(1)
   else
      lex = LEXOTHER
   return
   end
# lookup - locate name, extract definition from table
   integer function lookup(name, defn)
   character defn(MAXDEF), name(MAXTOK)
   integer i, j, k
   include clook
   for (i = lastp; i > 0; i = i - 1) {
      j = namptr(i)
      for (k = 1; name(k) == table(j) & name(k) ~= EOS; k = k + 1)
         j = j + 1
      if (name(k) == table(j)) {      # got one
         call scopy(table, j+1, defn, 1)
         lookup = YES
         return
         $@$
      $@$
   lookup = NO
   return
   end
# instal - add name and definition to table
   subroutine instal(name, defn)
   character defn(MAXTOK), name(MAXDEF)
   integer length
   integer dlen, nlen
   include clook
   nlen = length(name) + 1
   dlen = length(defn) + 1
   if (lastt + nlen + dlen > MAXTBL | lastp >= MAXPTR) {
      call putlin(name, ERROUT)
      call remark(": too many definitions.")
      $@$
   lastp = lastp + 1
   namptr(lastp) = lastt + 1
   call scopy(name, 1, table, lastt + 1)
   call scopy(defn, 1, table, lastt + nlen + 1)
   lastt = lastt + nlen + dlen
   return
   end
#block data
   block data
   include clook
   data lastp /0/
   data lastt /0/
   end
# ngetc - get a (possibly pushed back) character
   character function ngetc(c)
   character getc
   character c
   include cdefio
   if (bp > 0)
      c = buf(bp)
   else {
      bp = 1
      buf(bp) = getc(c)
      $@$
   if (c ~= EOF)
      bp = bp - 1
   ngetc = c
   return
   end
# otherc - output ordinary Fortran statement
   subroutine otherc(lexstr)
   character lexstr(ARB)
   call outtab
   call outstr(lexstr)
   call eatup
   call outdon
   return
   end
# outch - put one character into output buffer
   subroutine outch(c)
   character c
   integer i
   include coutln
   if (outp >= 72) {   # continuation card
      call outdon
      for (i = 1; i < 6; i = i + 1)
         outbuf(i) = BLANK
      outbuf(6) = STAR
      outp = 6
      $@$
   outp = outp + 1
   outbuf(outp) = c
   return
   end
# outcon - output "n   continue"
   subroutine outcon(n)
   integer n
#   string contin "continue"
   integer contin(9)
      data contin(1) /LETC/
      data contin(2) /LETO/
      data contin(3) /LETN/
      data contin(4) /LETT/
      data contin(5) /LETI/
      data contin(6) /LETN/
      data contin(7) /LETU/
      data contin(8) /LETE/
      data contin(9) /EOS/
   if (n > 0)
      call outnum(n)
   call outtab
   call outstr(contin)
   call outdon
   return
   end
# outdon - finish off an output line
   subroutine outdon
   include coutln
   outbuf(outp+1) = NEWLINE
   outbuf(outp+2) = EOS
   call putlin(outbuf, STDOUT)
   outp = 0
   return
   end
# outgo - output "goto  n"
   subroutine outgo(n)
   integer n
#   string goto "goto"
   integer goto(6)
      data goto(1) /LETG/
      data goto(2) /LETO/
      data goto(3) /LETT/
      data goto(4) /LETO/
      data goto(5) /BLANK/
      data goto(6) /EOS/
   call outtab
   call outstr(goto)
   call outnum(n)
   call outdon
   return
   end
define(MAXCHARS,10)
# outnum - output decimal number
   subroutine outnum(n)
   character chars(MAXCHARS)
   integer itoc
   integer i, len, n
   len = itoc(n, chars, MAXCHARS)
   for (i = 1; i <= len; i = i + 1)
      call outch(chars(i))
   return
   end
# outstr - output string
   subroutine outstr(str)
   character c, str(ARB)
   integer i, j
   for (i = 1; str(i) ~= EOS; i = i + 1) {
      c = str(i)
      if (c ~= SQUOTE & c ~= DQUOTE)
         call outch(c)
      else {
         i = i + 1
         for (j = i; str(j) ~= c; j = j + 1)   # find end
            ;
         call outnum(j-i)
         call outch(LETH)
         for ( ; i < j; i = i + 1)
            call outch(str(i))
         $@$
      $@$
   return
   end
# outtab - get past column 6
   subroutine outtab
   include coutln
   while (outp < 6)
      call outch(BLANK)
   return
   end
# parse - parse Ratfor source program
   subroutine parse
   character lexstr(MAXTOK)
   integer lex
   integer lab, labval(MAXSTACK), lextyp(MAXSTACK), sp, token
   call initkw   # install keywords in table
   sp = 1
   lextyp(1) = EOF
   for (token = lex(lexstr); token ~= EOF; token = lex(lexstr)) {
      if (token == LEXIF)
         call ifcode(lab)
      else if (token == LEXDO)
         call docode(lab)
      else if (token == LEXWHILE)
         call whilec(lab)
      else if (token == LEXDIGITS)
         call labelc(lexstr)
      else if (token == LEXELSE) {
         if (lextyp(sp) == LEXIF)
            call elseif(labval(sp))
         else
            call synerr("illegal else.")
         $@$
      if (token==LEXIF | token==LEXELSE | token==LEXWHILE
        | token==LEXDO | token==LEXDIGITS | token==LBRACE) {
         sp = sp + 1         # beginning of statement
         if (sp > MAXSTACK)
            call error("stack overflow in parser.")
         lextyp(sp) = token      # stack type and value
         labval(sp) = lab
         $@$
      else {      # end of statement - prepare to unstack
         if (token == RBRACE) {
            if (lextyp(sp) == LBRACE)
               sp = sp - 1
            else
               call synerr("illegal right brace.")
            $@$
         else if (token == LEXOTHER)
            call otherc(lexstr)
         else if (token == LEXBREAK | token == LEXNEXT)
            call brknxt(sp, lextyp, labval, token)
         token = lex(lexstr)      # peek at next token
         call pbstr(lexstr)
         call unstak(sp, lextyp, labval, token)
         $@$
      $@$
   if (sp ~= 1)
      call synerr("unexpected EOF.")
   return
   end
# pbstr - push string back onto input
   subroutine pbstr(in)
   character in(MAXLINE)
   integer length
   integer i
   for (i = length(in); i > 0; i = i - 1)
      call putbak(in(i))
   return
   end
# putbak - push character back onto input
   subroutine putbak(c)
   character c
   include cdefio
   bp = bp + 1
   if (bp > BUFSIZE)
      call error("too many characters pushed back.")
   buf(bp) = c
   return
   end
# ratfor - main program for Ratfor
   call parse
   stop
   end
# synerr - report Ratfor syntax error
   subroutine synerr(msg)
   character lc(MAXLINE), msg(MAXLINE)
   integer itoc
   integer junk
   include cline
   call remark("error at line .")
   junk = itoc(linect, lc, MAXLINE)
   call putlin(lc, ERROUT)
   call putch(COLON, ERROUT)
   call remark(msg)
   return
   end
# unstak - unstack at end of statement
   subroutine unstak(sp, lextyp, labval, token)
   integer labval(MAXSTACK), lextyp(MAXSTACK), sp, token
   for ( ; sp > 1; sp = sp - 1) {
      if (lextyp(sp) == LBRACE)
         break
      if (lextyp(sp) == LEXIF & token == LEXELSE)
         break
      if (lextyp(sp) == LEXIF)
         call outcon(labval(sp))
      else if (lextyp(sp) == LEXELSE) {
         if (sp > 2)
            sp = sp - 1
         call outcon(labval(sp)+1)
         $@$
      else if (lextyp(sp) == LEXDO)
         call dostat(labval(sp))
      else if (lextyp(sp) == LEXWHILE)
         call whiles(labval(sp))
      $@$
   return
   end
# whilec - generate code for beginning of while
   subroutine whilec(lab)
   integer labgen
   integer lab
   call outcon(0)    # unlabeled continue, in case there was a label
   lab = labgen(2)
   call outnum(lab)
   call ifgo(lab+1)
   return
   end
# whiles - generate code for end of while
   subroutine whiles(lab)
   integer lab
   call outgo(lab)
   call outcon(lab+1)
   return
   end
