文書整形 -- 指令の解析2016年12月05日 21:03

指令の解析は容易であり、comtyp()で行う。メインルーチンから呼び出される comand()から、最初にcomtyp()が呼び出される。comand()は、comtyp()の返す値に従い、 必要な処理を行っていく。comand()は以下の通りである。

RATFORでは、

# comand.r4 -- perform formatting command
      subroutine comand( buf )
      character buf(MAXLINE)
      integer comtyp, getval
      integer ct, spval, val
      integer argtyp

      include cpage.ri
      include cparam.ri

      ct = comtyp(buf)
      if (ct == UNKOWN) # igore unknown commands
          return

      val = getval(buf,argtyp)
      if (ct == FI) {
          call brk
          fill = YES
          }
      else if (ct == NF) {
          call brk
          fill = NOC
          }
      else if (ct == BR)
          call brk
      else if (ct == LS)
          call set(lsval,val,argtyp,1,1,HUGE)
      else if (ct == HE)
          call gettl(buf,header)
      else if (ct == FO)
          call gettl(buf,footer)
      else if (ct == SP) {
          call set(spval,val,argtyp,1,0,HUGE)
          call space(spval)
          }
      else if (ct == BP) {
          if (lineno > 0)
              call space(HUGE)
          call set(curpage,val,argtyp,curpage+1,-HUGE,HUGE)
          newpag = curpag
          }
      else if (ct == PL) {
          call set(plval,val,argtyp,PAGELEN,m1val+m2val+m3val+m4val+1,HUGE)
          bottom = plval - m3val - m4val
          }
      else if (ct == IN) {
          call set(inval,val,argtyp,0,0,rmval-1)
          tival = inval
          }
      else if (ct == RM)
          call set(rmval,val,argtyp,PAGEWIDTH,tival+1,HUDGE)
      else if (ct == TI) {
          call brk
          call set(tival,val,argtyp,0,0,rmval-1)
          }
      else if (ct == CE) {
          call brk
          call set(ceval,val,argtyp,1,0,HUGE)
          }
      else if (ct .eq. 14) then ! UL(14)
          call set(ulval,val,argtyp,1,0,HUGE)

      return
      end

WATCOM fortran 77では、

c comand.f -- perform formatting command
      subroutine comand( buf )
      integer*1 buf(82)                 ! MAXLINE(82)
      integer comtyp, getval
      integer ct, spval, val
      integer*1 argtyp

      include cpage.fi
      include cparam.fi

      ct = comtyp(buf)
      if (ct .eq. 0) then               ! UNKOWN(0)
          return
      end if

      val = getval(buf,argtyp)
      if (ct .eq. 4) then               ! FI(4)
          call brk
          fill = 1                      ! YES(1)
      else if (ct .eq. 9) then          ! NF(9)
          call brk
          fill = 0                      ! NOC(0)
      else if (ct .eq. 2) then          ! BR(2)
          call brk
      else if (ct .eq. 8) then          ! LS(8)
          call set(lsval,val,argtyp,1,1,1000) ! HUGE(1000)
      else if (ct .eq. 6) then          ! HE(6)
          call gettl(buf,header)
      else if (ct .eq. 5) then          ! FO(5)
          call gettl(buf,footer)
      else if (ct .eq. 12) then         ! SP(12)
          call set(spval,val,argtyp,1,0,1000) ! HUGE(1000)
          call space(spval)
      else if (ct .eq. 1) then          ! BP(1)
          if (lineno .gt. 0) then
              call space(1000)          ! HUGE(1000)
          end if
          call set(curpage,val,argtyp,curpage+1,-1000,1000) ! HUGE(1000)
          newpag = curpag
      else if (ct .eq. 10) then         ! PL(10)
          call set(plval,val,argtyp,66, ! PAGELEN(66)
     1        m1val+m2val+m3val+m4val+1,1000) ! HUGE(1000)
          bottom = plval - m3val - m4val
      else if (ct .eq. 7) then          ! IN(7)
          call set(inval,val,argtyp,0,0,rmval-1)
          tival = inval
      else if (ct .eq. 11) then         ! RM(11)
          call set(rmval,val,argtyp,60,tival+1,1000) ! PAGEWIDTH(60) HUGE(1000)
      else if (ct .eq. 13) then         ! TI(13)
          call brk
          call set(tival,val,argtyp,0,0,rmval-1) 
      else if (ct .eq. 3) then          ! CE(3)
          call brk
          call set(ceval,val,argtyp,1,0,1000) ! HUGE(1000)
      else if (ct .eq. 14) then ! UL(14)
          call set(ulval,val,argtyp,1,0,1000) ! HUGE(1000)
      end if

      return
      end

実際の指令の解析は、comtyp()が行う。

RATFOR版は、以下の通り。

c comtyp.r4 -- decode command type
      integer function comtyp(buf)
      character buf(MAXLINE)

      if (buf(2) == LETB & buf(3) == LETP)
         comtyp = BP
      else if (buf(2) == LETB & buf(3) == LETR)
         comtyp = BR
      else if (buf(2) == LETC & buf(3) == LETE)
         comtyp = CE
      else if (buf(2) == LETF & buf(3) == LETI)
         comtyp = FI
      else if (buf(2) == LETF & buf(3) == LETO)
         comtyp = FO
      else if (buf(2) == LETH & buf(3) == LETE)
         comtyp = HE
      else if (buf(2) == LETI & buf(3) == LETN)
         comtyp = IN
      else if (buf(2) == LETL & buf(3) == LETS)
         comtyp = LS
      else if (buf(2) == LETN & buf(3) == LETF)
         comtyp = NF
      else if (buf(2) == LETP & buf(3) == LETL)
         comtyp = PL
      else if (buf(2) == LETR & buf(3) == LETM)
         comtyp = RM
      else if (buf(2) == LETS & buf(3) == LETP)
         comtyp = SP
      else if (buf(2) == LETT & buf(3) ==  LETI)
         comtyp = TI
      else if (buf(2) == LETU & buf(3) == LETL)
         comtyp = UL
      else
         comtyp = UNKOWN
      end if
      return
      end

WATCOM fortran77版は、以下の通り。

c comtyp.for -- decode command type
      integer function comtyp(buf)
      integer*1 buf(82)                 ! MAXLINE(82)

      if ((buf(2) .eq. 98) .and. (buf(3) .eq. 112)) then      ! LETB('b',98) LETP('p',112)
         comtyp = 1                         ! BP(1)
      else if ((buf(2) .eq. 98) .and. (buf(3) .eq. 114)) then ! LETB('b',98) LETR('r',114)
         comtyp = 2                     ! BR(2)
      else if ((buf(2) .eq. 99) .and. (buf(3) .eq. 101)) then ! LETC('c',99) LETE('r',101)
         comtyp = 3                     ! CE(3)
      else if ((buf(2) .eq. 102) .and. (buf(3) .eq. 105)) then ! LETF('f',102) LETI('i',105)
         comtyp = 4                     ! FI(4)
      else if ((buf(2) .eq. 102) .and. (buf(3) .eq. 111)) then ! LETF('f',102) LETO('o',111)
         comtyp = 5                     ! FO(5)
      else if ((buf(2) .eq. 104) .and. (buf(3) .eq. 101)) then ! LETH('h',104) LETE('e',101)
         comtyp = 6                     ! HE(6)
      else if ((buf(2) .eq. 105) .and. (buf(3) .eq. 110)) then ! LETI('i',105) LETN('n',110)
         comtyp = 7                     ! IN(7)
      else if ((buf(2) .eq. 108) .and. (buf(3) .eq. 115)) then ! LETL('l',108) LETS('n',115)
         comtyp = 8                     ! LS(8)
      else if ((buf(2) .eq. 110) .and. (buf(3) .eq. 102)) then ! LETN('n',110) LETF('f',102)
         comtyp = 9                     ! NF(9)
      else if ((buf(2) .eq. 112) .and. (buf(3) .eq. 108)) then ! LETP('n',112) LETL('l',108)
         comtyp = 10                    ! PL(10)
      else if ((buf(2) .eq. 114) .and. (buf(3) .eq. 109)) then  ! LETR('r',114) LETM('m',109)
         comtyp = 11                    ! RM(11)
      else if ((buf(2) .eq. 115) .and. (buf(3) .eq. 112)) then  ! LETS('s',115) LETP('p',112)
         comtyp = 12                    ! SP(12)
      else if ((buf(2) .eq. 116) .and. (buf(3) .eq. 105)) then  ! LETT('t',116) LETI('i',105)
         comtyp = 13                    ! TI(13)
      else if ((buf(2) .eq. 117) .and. (buf(3) .eq. 108)) then  ! LETU('u',117) LETL('l',108)
         comtyp = 14                    ! UL(14)
      else
         comtyp = 0                     ! UNKOWN(0)
      end if
      return
      end

指令の引数は、getval()で取得する。取得した値は、set()で設定する。

getval()のRATFOR版は、以下の通り。

# getval.r4 - evaluate optional numeric argument
      integer function getval(buf,argtyp)
      character buf(MAXLINE)
      integer ctoi
      integer argtyp, i

      i = 1                        # skip command name
      while (buf(i) != BLANK & buf(i) != TAB & buf(i) != NEWLINW)
          i = i + 1
      call skipbl(buf,i)           # find argument
      argtyp = buf(i)
      if (argtyp == PLUS | argtyp == MINUS)
         i = i + 1
      getval = ctoi(buf,i)
      return
      end

WATCOM fortran版は、以下の通り。

c getval.for - evaluate optional numeric argument
      integer function getval(buf,argtyp)
      integer*1 buf(82)                 ! MAXLINE(82)

      integer ctoi
      integer argtyp, i

      i = 1                             ! skip command name
      while ((buf(i) .ne. 32)           ! BLANK(32)
     1    .and. (buf(i) .ne. 9)         ! TAB(9)
     2    .and. (buf(i) .ne. 10)) do    ! NEWLINE(10)
          i = i + 1
      end while
      call skipbl(buf,i)                ! find argument
      argtyp = buf(i)
      if ((argtyp .eq. 43) .or. (argtyp .eq. 45)) then ! PLUS('+',43) MINUS('-',45)
         i = i + 1
      end if
      getval = ctoi(buf,i)
      return
      end

set()のRATFOR版は、以下の通り。

# set.r4 -- set parameter and check range
      subroutine set( param, val, argtyp, defval, minval, maxval )
      integer param, val, defval, minval, maxval
      character argtyp
      integer max,min

      if (argtyp == NEWLINE)
         param = defval
      else if (argtyp == PLUS)
         param = param + val
      else if (argtyp == MINUS)
         param = param - val
      else
         param = val

      param = min( param, maxval )
      param = max( param, minval )

      return
      end

WATCOM fortran版は、以下の通り。

c set.for -- set parameter and check range
      subroutine set( param, val, argtyp, defval, minval, maxval )
      integer param, val, defval, minval, maxval
      integer*1 argtyp
      integer max,min

      if (argtyp .eq. 10) then          ! defaulted  NEWLINE(10)
         param = defval
      else if (argtyp .eq. 43) then     ! relative + PLUS('+',43)
         param = param + val
      else if (argtyp .eq. 45) then     ! relative - NIMUS('-',45)
         param = param - val
      else
         param = val
      endif
      param = min( param, maxval )
      param = max( param, minval )

      return
      end

文書整形 -- テキストの書き出し2016年12月10日 18:40

指令以外の行は、テキストである。これを書き出すルーチンtext()の 臨時第一版を示す。この版は下請けルーチンput()を呼び出すだけである。

RATFORでは、

# text.r4 -- process text lines (interim version 1)
      subroutine text(inbuf)
      character inbuf(INSIZE)

      call put(inbuf)
      return
      end

WATCOM fortran 77では、

c text.f -- process text lines (interim version 1)
      subroutine text(inbuf)
      integer*1 inbuf(82)               ! INSIZE(82)

      call put(inbuf)
      return
      end

下請けルーチンput()では、ページのレイアウトを考慮した出力が作り出される。

RATFOR版は、以下の通り。

# put.r4 -- put out line with proper spacing and indenting
      subroutine put(buf)
      character buf(MAXLINE)
      integer min
      integer i
      include cpage.ri
      include cparam.ri

      if (lineno == 0 | lineno > bottom)
          call phead

      for (i = 1; i <= tival; i = i + 1) # indenting
          call putc(BLANK)
      tival = inval
      call putlin(buf,STDOUT)
      call skip(min(lsval-1,bottom-lineno))
      lineno = lineno + lsval
      if (lineno > bottom)
          call pfoot
      return
      end

WATCOM fortran77版は、以下の通り。

c put.f -- put out line with proper spacing and indenting
      subroutine put(buf)
      integer*1 buf(82)                 ! MAXLINE(82)
      integer min
      integer i
      include cpage.fi
      include cparam.fi

      if ((lineno .eq. 0) .or. (lineno .gt. bottom)) then
          call phead
      end if

      i = 1                             ! indenting
      while (i .le. tival) do
          call putc(32)                 ! BLANK(32)
          i = i + 1
      end while

      tival = inval
      call putlin(buf,6)                ! STDOUT(6)
      call skip(min(lsval-1,bottom-lineno))
      lineno = lineno + lsval

      if (lineno .gt. bottom) then
          call pfoot
      end if
      return
      end

ページ見出し、ヘッダーとフッターは、phead()、pfoot()で書き出す。 put()は、これらを適当な位置で書き出すよう制御する。

phead()のRATFOR版は、以下の通り。

# phead.r4 -- put out page header
      subroutine phead
      include cpage.ri

      curpag = newpag
      newpag = newpag + 1
      if (m1val > 0) {
         call skip(m1val - 1)
         call puttl(header,curpag)
         }
      call skip(m2val)
      lineno = m1val + m2val + 1
      return
      end

WATCOM fortran 77版は、以下の通り。

c phead.f -- put out page header
      subroutine phead
      include cpage.fi

      curpag = newpag
      newpag = newpag + 1
      if (m1val .gt. 0) then
         call skip(m1val-1)
         call puttl(header,curpag)
      end if
      call skip(m2val)
      lineno = m1val + m2val + 1
      return
      end

pfoot()のRATFOR版は、以下の通り。

# pfoot.r4 -- put out page footer
      subroutine pfoot
      include cpage.ri

      call skip(m3val)
      if (m4val > 0) {
         call puttl(footer,curpag)
         call skip(m4val - 1)
         }

      return
      end

WATCOM fortran 77版は、以下の通り。

c pfoot.f -- put out page footer
      subroutine pfoot
      include cpage.fi

      call skip(m3val)
      if (m4val .gt. 0) then
         call puttl(footer,curpag)
         call skip(m4val - 1)
      endif

      return
      end

phead()、pfoot()とも、ページ見出しの書き出しはputtl()を使用する。 puttl()は、書き出す内容にPAGEMUN記号("#")が含まれていた場合、 その場所にページ番号を入れ込む。

puttl()のRATFOR版は、以下の通り。

# puttl.r4 -- put title line with optional page number
      subroutine puttl(buf,pageno)
      character buf(MAXLINE)
      integer pageno
      integer i

      for (i = 1; buf(i) != EOS; i = i + 1)
          if (buf(i) == PAGENUM)
              call putdec(pageno,1)
          else
              call putc(buf(i))
      return
      end

WATCOM fortran 77版は、以下の通り。

c puttl.for -- put title line with optional page number
      subroutine puttl(buf,pageno)
      integer*1 buf(82)                 ! MAXLINE(82)
      integer pageno
      integer i

      i = 1
      while (buf(i) .ne. -2) do         ! EOS(-2)
          if (buf(i) .eq. 35) then      ! PAGENUM('#',35)
              call putdec(pageno,1)
          else
              call putc(buf(i))
          end if
          i = i + 1
      end while
      return
      end

ページ見出しは、gettl()でページ見出し用のバッファーにセットする。

gettl()のRATFOR版は、以下の通り。

# gettl.r4 -- copy title from buf to ttl
      subroutine gettl(buf,ttl)
      character buf(MAXLINE),ttl(MAXLINE)
      integer i

      i = 1                             # skip command name
      while ( buf(i) != BLANK & buf(i) != TAB & buf(i) != NEWLINE )
          i = i + 1
      call skipbl(buf,i)                # find argument
      if (buf(i) == SQUORT | buf(i) == DQUORT) # strip quorte if found
          i = i + 1
      call scopy(buf, i, ttl, 1)
      return
      end

WATCOM fortran 77版は、以下の通り。

c gettl.for -- copy title from buf to ttl
      subroutine gettl(buf,ttl)
      integer*1 buf(82),ttl(82)         ! MAXLINE(82) MAXLINE(82)
      integer i

      i = 1                             ! skip command name
      while ((buf(i) .ne. 32)           ! BLANK(32)
     1    .and. (buf(i) .ne. 9)         ! TAB(9)
     2    .and. (buf(i) .ne. 10)) do    ! NEWLINE(10)
          i = i + 1
      end while

      call skipbl(buf,i)                ! find argument
      if ((buf(i) .eq. 39)
     1    .or. (buf(i) .eq. 34)) then   ! strip quorte if found SQUOTE(''',39) DQUOTE('"',34)
          i = i + 1
      end if
      call scopy(buf,i,ttl,1)
      return
      end

".sp"指令や".bp"指令は、space()で空行を出力しページレイアウトを 調整する。

space()のRATFOR版は、以下の通り。

# space.r4 -- space n lines or to bottom of page
      subroutine space(n)
      integer n
      integer min
      include cpage.ri

      call brk
      if (lineno > bottom)
          return
      if (lineno == 0)
          call phead
      call skip(min(n, bottom + 1 - lineno))
      lineno = lineno + n
      if (lineno > bottom)
          call pfoot
      return
      end

WATCOM fortran 77版は、以下の通り。

c space.f -- space n lines or to bottom of page
      subroutine space(n)
      integer n
      integer min
      include cpage.fi

      call brk
      if (lineno .gt. bottom) then
          return
      end if
      if (lineno .eq. 0) then
          call phead
      end if
      call skip(min(n,bottom+1-lineno))
      lineno = lineno + n
      if (lineno .gt. bottom) then
          call pfoot
      end if
      return
      end

文書整形 -- 出力の右揃えなど2016年12月14日 18:13

現在の出力は、行の右端が不揃いになる。これを解消するのに、 putwrd()を修正する。この中のspread()は、語の間の空白を調整し一行の中に、語を 割り付ける。

RATFORでは

# putwrd.r4 -- put a word in outbuf; include margin justification
      subroutine putwrd(wrdbuf)
      character wrdbuf(INSIZE)
      integer length,width
      integer last,llval,nextra,w
      include cout.fi
      include cparam.fi

      w = width(wrdbuf)
      last = length(wrdbuf) + outp + 1  ! new end of outbuf
      llval = rmval - tival
      if ((outp > 0) & (outw+w > llval | last >= MAXOUT)) { # too big
          last = last - outp            # remember end of wrdbuf
          nextra = llval - outw + 1
          call spread(outbuf,outp,nextra,outwds)
          if ((nextra > 0) & (outwds > 1))
               outp = outp + nextra

          call brk                      # flush previous line
          }
      call scopy(wrdbuf,1,outbuf,outp+1)
      outp = last
      outbuf(outp) = BLANK              # blank between words
      outw = outw + w + 1               # 1 for blank
      outwds = outwds + 1
      return
      end

WATCOM fortran 77では、

c putwrd.f -- put a word in outbuf; include margin justification
      subroutine putwrd(wrdbuf)
      integer*1 wrdbuf(82)              ! INSIZE(82)
      integer length,width
      integer last,llval,nextra,w
      include cout.fi
      include cparam.fi

      w = width(wrdbuf)
      last = length(wrdbuf) + outp + 1  ! new end of outbuf
      llval = rmval - tival
      if ((outp .gt. 0) .and.
     1    ((outw+w .gt. llval) .or. (last .ge. 74))) then ! MAXOUT(74)  too big
          last = last - outp            ! remember end of wrdbuf
          nextra = llval - outw + 1
          call spread(outbuf,outp,nextra,outwds)
          if ((nextra .gt.0) .and. (outwds .gt. 1)) then
               outp = outp + nextra
          end if
          call brk                      ! flush previous line
      end if
      call scopy(wrdbuf,1,outbuf,outp+1)
      outp = last
      outbuf(outp) = 32                 ! BLANK(32)       blank between words
      outw = outw + w + 1               ! 1 for blank
      outwds = outwds + 1
      return
      end

spread()は、以下の通り

RATFORでは

# spread.r4 -- spread words to justify right margin
      subroutine spread(buf,outp,nextra,outwds)
      character buf(MAXOUT)
      integer outp,nextra,outwds
      integer min
      integer dir,i,j,nb,ne,nholes
      data dir/0/

      if ((nextra <= 0) | (outwds <= 1))
          return
      dir = 1 - dir                     # reverce previouse direction
      ne = nextra
      nholes = outwds - 1
      i = outp - 1
      j = min(MAXLINE - 2, i + ne)      # leave room for NEWLINE, EOS
      while (i < j) {
          buf(j) = buf(i)
          if (buf(i) == BLANK) {
              if (dir == 0)
                  nb = (ne - 1) / nholes + 1
              else
                  nb = ne / nholes
              ne = ne - nb
              nholes = nholes - 1
              for ( ; nb > 0; nb = nb - 1) {
                  j = j - 1
                  buf(j) = BLANK
                  }
              }
          i = i - 1
          j = j - 1
          }
      return
      end

WATCOM fortran 77では、

c spread.for -- spread words to justify right margin
      subroutine spread(buf,outp,nextra,outwds)
      integer*1 buf(74)                 ! MAXOUT(74)
      integer outp,nextra,outwds
      integer min
      integer dir,i,j,nb,ne,nholes
      data dir/0/

      if ((nextra .le. 0) .or. (outwds .le. 1)) then
         return
      end if
      dir = 1 - dir                     ! reverce previouse direction
      ne = nextra
      nholes = outwds - 1
      i = outp - 1
      j = min(74-2, i+ne)               ! MAXOUT(74) -2 for leave room for NEWLINE, EOS

      while (i .lt. j) do
          buf(j) = buf(i)
          if (buf(i) .eq. 32) then      ! BLANK(32)
              if (dir .eq. 0) then
                  nb = (ne - 1) / nholes + 1
              else
                  nb = ne / nholes
              end if
              ne = ne - nb
              nholes = nholes - 1
              while (nb .gt. 0) do
                  j = j - 1
                  buf(j) = 32           ! BLANK(32)
                  nb = nb - 1
              end while
          end if
          i = i - 1
          j = j - 1
      end while
      return
      end

中央そろえは、center()で行う。実際は、一時字下げの値を調節する。

RATFORでは

# center.r4 -- center a line by setting tival
      subroutine center(buf)
      character buf(ARB)
      integer width,max
      include cparam.fi

      tival = max((rmval + tival - width(buf)) / 2, 0)
      return
      end

WATCOM fortran 77では、

c center.f -- center a line by setting tival
      subroutine center(buf)
      integer*1 buf(9999)               ! ARB(9999)
      integer width,max
      include cparam.fi

      tival = max((rmval + tival - width(buf)) / 2, 0)
      return
      end

下線は、書き出し文字とBACKSPACE、UNDERLINEを組み合わせ作り出す。 実際は、underl()で書き出し文字列を作り出す。

RATFORでは

# underl.r4 -- underline a line
      subroutine underl(buf,tbuf,size)
      character buf(size),tbuf(size)
      integer size
      integer type
      integer i,j,t

      j = 1
      for (i = 1; buf(i) != NEWLINE & j < size- 1; i = i + 1) {
          tbuf(j) = buf(i)
          j = j + 1
          if (buf(i) != BLANK & buf(i) != TAB & buf(i) != BACKSPACE) {
              tbuf(j) = BACSPACE
              tbuf(j+1) = UNDERLINE
              j = j + 2
              }
          }
      tbuf(j) = NEWLINE
      tbuf(j+1) = EOS
      call scopy(tbuf, 1, buf, 1)          # copy it back to buf
      return
      end

WATCOM fortran 77では、

c underl.for -- underline a line
      subroutine underl(buf,tbuf,size)
      integer*1 buf(size),tbuf(size)
      integer size
      integer i,j,t

      j = 1
      i = 1
      while ((buf(i) .ne. 10) .and. (j .lt. size-1)) do ! NEWLINE(10)
          tbuf(j) = buf(i)
          j = j + 1
          if ((buf(i) .ne. 32)          ! BLANK(32)
     1        .and. (buf(i) .ne. 9)     ! TAB(9)
     2        .and. (buf(i) .ne. 8)) then ! BACKSPACE(8)
              tbuf(j)   = 8             ! BACKSPACE(8)
              tbuf(j+1) = 95            ! UNDERLINE(95)
              j = j + 2
          end if
          i = i + 1
      end while
      tbuf(j)   = 10                    ! NEWLINE(10)
      tbuf(j+1) = -2                    ! EOS(-2)
      call scopy(tbuf,1,buf,1)          ! copy it back to buf
      return
      end

ここまで出てきた新機能を追加するには、text()を修正する必要がある。 text()の最終版は、以下の通り。

RATFORでは

# text.r4 -- process text lines (final version)
      subroutine text(inbuf)
      character inbuf(INSIZE), wrdbuf(INSIZE)
      integer getword
      integer i
      include cparam.ri

      if (inbuf(1) == BLANK | inbuf(1) == NEWLINE)
         call leadbl(inpuf)             # move left, set tival
      if (ulval > 0) {                  # underlining
          call underl(inbuf,wrdbuf,INSIZE)
          ulval = ulval - 1
          }
      if (ceval > 0) {                  # centering
          call center(inbuf)
          call put(inbuf)
          ceval = ceval - 1
          }
      else if (inbuf(1) == NEWLINE)     # all blank line
         call put(inbuf)
      else if (fill == NO)              # unfiled text
         call put(inbuf)
      else                              # filled text
         for (i = 1;getwrd(inbuf,i,wrdbuf)>0; )
             call putwrd(wrdbuf)
      return
      end

WATCOM fortran 77では、

c text.f -- process text lines (final version)
      subroutine text(inbuf)
      integer*1 inbuf(82), wrdbuf(82)   ! INSIZE(82) INSIZE(82)
      integer getwrd
      integer i
      include cparam.fi

      if (inbuf(1) .eq. 32 .or. inbuf(1) .eq. 10) then ! BLANK(32) NEWLINE(10)
         call leadbl(inpuf)             ! move left, set tival
      end if
      if (ulval .gt. 0) then            ! underlining
          call underl(inbuf,wrdbuf,INSIZE)
          ulval = ulval - 1
      end if
      if (ceval .gt. 0) then            ! centering
          call center(inbuf)
          call put(inbuf)
          ceval = ceval - 1
      else if (inbuf(1) .eq. 10) then   ! all blank line
         call put(inbuf)
      else if (fill .eq. 0) then        ! unfiled text NO(0)
         call put(inbuf)
      else                              ! filled text
         i = 1
         while (getwrd(inbuf,i,wrdbuf) .gt. 0 ) do
             call putwrd(wrdbuf)
         end while
      end if
      return
      end

マクロ処理 -- 文字列の置換版2016年12月31日 20:06

これまで紹介したRATFORのプログラムリストには、記号定数が ふんだんに使われてきた。記号定数は最終的には、定数に置換する必要がある。 これを行う事をマクロ展開という。また、それを行うプログラムをマクロプロセッサー という。このプログラムは、プリグラム中に書き込まれたマクロを展開し書き出す。

まずはじめに、文字列の置換版 -- define を紹介する。

defineでの、マクロの定義は次のようになる。

       define(EOF,-1)

このように定義されたマクロは、ソースファイルに下記のように使われる。

       define(EOF,-1)
       
       program copy # copy from STDIN to STDOUT
       
       call initfile
       
       while(getc(c) != EOF)
           call putc(c)
       stop
       end

これをマクロ展開すると、

       
       
       program copy # copy from STDIN to STDOUT
       
       call initfile
       
       while(getc(c) != -1)
           call putc(c)
       stop
       end

このようなカンタンな場合から、マクロプロセッサーをはじめる。

マクロプロセッサーのあらすじは、次のようになるだろう。

      while(gettok(綴り) != EOF) {
          綴りの表を引く
          if (綴り == "define")
              新しい綴りとその値を登録
          else if (綴りが表にあった)
              入力を綴りに対応する置き換え文字列に切り替える
          else
              綴りをそのまま出力する

ここで、gettok()は以下の通り。

RATFOR版は、

# gettok.r4 -- get alphanumeric string non-alpha for define
      character function gettok(token,toksiz)
      integer toksiz
      character token(toksiz)
      character ngetc,type
      integer i

      for (i = 1; i < toksiz; i = i + 1) {
          gettok = type(ngetc(token(i)))
          if (gettok != LETTER) & (gettok != DIGIT)
              break
          }

      if (i >= toksiz)
          call error('token too long.')

      if (i > 1) {            # some alpha was seen
          call putbak(token(i))
          i = i - 1
          gettok = ALPHA
      # else single character token
      token(i+1) = EOS
      return
      end

RATFOR版は、

c  gettok.for -- get alphanumeric string or single non-alpha for define
      integer*1 function gettok(token,toksiz)
      integer toksiz
      integer*1 token(toksiz)
      integer*1 ngetc,type
      integer i

      i = 1
      while (i .lt. toksiz) do
          gettok = type(ngetc(token(i)))
          if ((gettok .ne. 97) .and.    ! ALPHA(97)
     1        (gettok .ne. 48)) then    ! DIGIT(48)
              exit
          end if
          i = i + 1
      end while

      if (i .ge. toksiz) then
          call error('token too long.')
      end if

      if (i .gt. 1) then                ! some alpha was seen
          call putbak(token(i))
          i = i - 1
          gettok = 97                   ! ALPHA(97)
      ! else single character token
      end if
      token(i+1) = -2                   ! EOS(-2)
      return
      end

gettok()では、綴り取り出すときに、先読みを行う。当然、先読みした分は、 元に戻す必要もある。これらの統一的に行うのに、ngetc()、putbak()を使う。

読みすぎた文字は、putbak()で元に戻す。putbak()は、ngetc()と共有の バッファーを持っている。このバッファーには、putbak()で戻された文字が 蓄えられる。ngetc()はこのバッファーに残りがあれば、そこから文字を取り出し、 さもなくば、getc()で文字を読み込む。

putbak()は、以下の通り。

RATFOR版は、

# putbak.r4 -- push character back onto input
      subroutine putbak(c)
      character c
      include cdefio.ri
      
      bp = bp + 1
      if (bp > BUFSIZE)
         call error('too many character pushed back.')
      buf(bp) = c
      return
      end

WATCOM fortran 77版は、

c putbak.f -- push character back onto input
      subroutine putbak(c)
      integer*1 c
      include cdefio.fi
      
      bp = bp + 1
      if (bp .gt. 1000) then            ! BUFSIZE(1000)
         call error('too many character pushed back.')
      end if
      buf(bp) = c

      return
      end

putbak()、ngetc()共通のデータ領域cdefioは、以下の通り。

RATFOR版は、

# cdefio.ri
      common /cdefio/bp,buf(BUFSIZE)
      integer bp    # next available character; init = 0
      character buf # pushed back character

WATCOM fortran 77版は、

c cdefio.fi
      common /cdefio/bp,buf(1000)       ! BUFSIZE(1000)
      integer bp    ! next available character; init = 0
      character buf ! pushed back character

文字列を入力に送り返すことは、多々あるわけでないが、putbak()を 複数回呼び出すことで、実現できる。pbstr()は、以下の通り。

RATFOR版は、

# pbstr.r4 -- 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

WATCOM fortran 77版は、

c pbstr.for -- push string back onto input
      subroutine pbstr(in)
      integer*1 in(82)                  ! MAXLINE(82)
      integer length
      integer i

      i = length( in )
      while (i .gt. 0) do
          call putbak(in(i))
          i = i - 1
      end while
      return
      end

ngetc()は、以下の通り。

RATFOR版は、

# ngetc.r4 -- get a (possibly pushed back) character
      character function ngetc( c )
      character c
      character getc
      include cdefio.ri

      if (bp > 0)
         c = buf(bp)
      else {
         bp = 1
         buf(bp) = getc(c)
         }
      if (c != EOF)
         bp = bp - 1
      ngetc = c
      return
      end

WATCOM fortran 77版は、

!  ngetc.f -- get a (possibly pushed back) character
      integer*1 function ngetc( c )
      integer*1 c
      integer*1 getc

      include cdefio.fi

      if (bp .gt. 0) then
         c = buf(bp)
      else
         bp = 1
         buf(bp) = getc(c)
      end if
      if (c .ne. -1) then               ! EOF(-1)
         bp = bp - 1
      end if
      ngetc = c
      return
      end

cdefioの初期化は、initbuf()で行う。

RATFOR版は、

# initbuf.r4
      subroutine initbuf
      include cdefio.ri

      bp = 0
      buf(1) = EOS
      return
      end

WATCOM fortran 77版は、

c initbuf.f
      subroutine initbuf
      include cdefio.fi

      bp = 0
      buf(1) = -2                       ! EOS(-2)
      return
      end

defineのメインルーチンは、以下の通りである。

gettok()で 綴りを切り出す。gettok()がALPHA以外を返したら、マクロではないから、そのまま、 出力する。綴りの表は、lookup()を使って引く。表に載っていなかったら、それは、 そのまま出力する。表に載っていて、それがDEFTYPEであったら、新しいマクロを getdef()で取り出し。マクロ表にinstall()で登録する。 登録されているマクロ名であれば、置き換え文字列を 入力に送り返す。

RATFOR版は以下の通り。

# define.r4 -- simple string replacement macro processor
      program define
      integer gettok
      character defn(MAXDEF),t,token(MAXTOK)
      integer lookup

      string defnam "define"
      character deftyp(2)
      data deftyp(1)/DEFTYPE/,deftyp(2)/EOS/

      call initfile
      call inittbl
      call initbuf
      call instal(defnam,deftyp)

      for(t = gettok(token,MAXTOK);t != EOF;t = gettok(token,MAXTOK))
          if (t != ALPHA)               # output non-alpha tokens
              call putlin(token,STDOUT)
          else if (lookup(token,defn) == NO)
              call putlin(token,STDOUT)
          else if (defn(1) == DEFTYPE) then # get definition
              call getdef(token,MAXTOK,defn,MAXTOK)
              call instal(token,defn)
          else
              call pbstr(defn)          # push replacement
          end if
      end while
      stop
      end

WATCOM fortran 77版は以下の通り。

c define.f -- simple string replacement macro processor
      program define
      integer gettok
      integer*1 defn(82),t,token(82)    ! MAXDEF(82) MAXTOK(82)
      integer lookup
      integer*1 defnam(7)
      character $defnam(7)
      equivalence (defnam,$defnam)
      data $defnam(1)/'d'/
      data $defnam(2)/'e'/
      data $defnam(3)/'f'/
      data $defnam(4)/'i'/
      data $defnam(5)/'n'/
      data $defnam(6)/'e'/
      data defnam(7)/-2/                ! EOS(-2)
      integer*1 deftyp(2)
      data deftyp(1)/-4/,deftyp(2)/-2/  ! DEFTYPE(-4) EOS(-2)

      call initfile
      call inittbl
      call initbuf
      call instal(defnam,deftyp)

      t = gettok(token,82)              ! MAXTOK(82)
      while (t .ne. -1) do              ! EOF(-1)
          if (t .ne. 97) then           ! ALPHA(97) output non-alpha tokens
              call putlin(token,6)      ! STDOUT(6)
          else if (lookup(token,defn) .eq. 0) then ! NO(0) and undefined
              call putlin(token,6)      ! STDOUT(6)
          else if (defn(1) .eq. -4) then ! DEFTYPE(-4) get definition
              call getdef(token,82,defn,82) ! MAXTOK(82) MAXDEF(82)
              call instal(token,defn)
          else
              call pbstr(defn)          ! push replacement
          end if
          t = gettok(token,82)          ! MAXTOK(82)
      end while
      stop
      end

マクロを取り出すgetdef()は、以下の通り。

RATFOR版は以下の通り。

# getdef.r4 (for no argument) -- get name and definition
      subroutine getdef(token,toksiz,defn,defsiz)
      integer toksiz, defsiz
      character token(toksiz),defn(defsiz)
      character gettok,ngetc
      character c
      integer i,nlpar

      if (ngetc(c) != LPAREN)
          call error('missing left paren.')
      else if (gettok(token,toksiz) != ALPHA)
          call error('non-alphanumeric name.')
      else if (ngetc(c) != COMMA)
          call error('missing comma in DEFINE.')
      ! else got (name,

      nlpar = 0
      for (i = 1;nlpar >= 0; i = i + 1)
          if (i > defsiz)
              call error('definition too long.')
          else if (ngetc(defn(i)) == EOF)
              call error('missing right paren.')
          else if (defn(i) == LPAREN)
              nlpar = nlpar + 1
          else if (defn(i) == RPAREN
              nlpar = nlpar - 1
          ! else normal character indefn(i)
      defn(i-1) = EOS
      return
      end

WATCOM fortran 77版は以下の通り。

c getdef.f (for no argument) -- get name and definition
      subroutine getdef(token,toksiz,defn,defsiz)
      integer toksiz, defsiz
      integer*1 token(toksiz),defn(defsiz)
      integer*1 gettok,ngetc
      integer*1 c
      integer i,nlpar

      if (ngetc(c) .ne. 40) then        ! LPAREN(40)
          call error('missing left paren.')
      else if (gettok(token,toksiz) .ne. 97) then ! ALPHA(97)
          call error('non-alphanumeric name.')
      els eif (ngetc(c) .ne. 44) then   ! COMMA(44)
          call error('missing comma in DEFINE.')
      ! else got (name,
      end if

      nlpar = 0
      i = 1
      while (nlpar .ge. 0) do
          if (i .gt. defsiz) then
              call error('definition too long.')
          else if (ngetc(defn(i)) .eq. -1) then ! EOF(-1)
              call error('missing right paren.')
          else if (defn(i) .eq. 40) then ! LPAREN(40)
              nlpar = nlpar + 1
          else if (defn(i) .eq. 41) then ! RPAREN(41)
              nlpar = nlpar - 1
          ! else normal character in defn(i)
          end if
          i = i + 1
      end while
      defn(i-1) = -2                    ! EOS(-2)
      return
      end