Ratforプリプロセッサー -- コード生成 "do" ― 2017年06月20日 18:06
Ratforのdo文にであったら、doの限界指定部を取り出して、ラベルL、L+1を 作りだし、
do L 限界指定部
を出力します。そして、doの終わりに達したら、
L continue
L+1 continue
を出力します。ここで、ラベルL+1は、breakに出会ったときの行き先になります。また、ラベルLは、
do分のループの終わりを示すとともに、nextに出会った時の行き先になります。
具体的には、docode()でdo文のはじめを生成します。
docode()のRatofor版は以下の通り。
# docode.r4 -- generate code for beginning of do
include ratfor.def
subroutine docode(lab)
integer lab
integer labgen
string(dostr,"do")
call outtab
call outstr(dostr)
call outch(BLANK)
lab = labgen(2)
call outnum(lab)
call outch(BLANK)
call eatup
call outdon
return
end
WATCOM Fortran77版は以下の通り。
c docode.f -- generate code for beginning of do
include ratfor.def
subroutine docode(lab)
integer lab
integer labgen
string(dostr,"do")
call outtab
call outstr(dostr)
call outch(BLANK)
lab = labgen(2)
call outnum(lab)
call outch(BLANK)
call eatup
call outdon
return
end
eatup()は、まだ取り込んでいない部分を継続行を含めて処理します。
eatup()のRatofor版は以下の通り。
# eatup.r4 -- process rest of statement; interpret continuations
include ratfor.def
subroutine eatup
character gtoken
character ptoken(MAXTOK),t,token(MAXTOK)
integer nlpar
nlpar = 0
repeat `
t = gtoken(token,MAXTOK)
if (t == SEMICOL | t == NEWLINE)
break
if (t == LBRACE) {
call pbstr(token)
break
}
if (t == RBRACE | t == EOF) {
call synerr('unexpected brace or EOF.')
call pbstr(token)
break
}
if (t == COMMA) {
if (gtoken(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
WATCOM Fortran77版は以下の通り。
c eatup.f -- process rest of statement; interpret continuations
include ratfor.def
subroutine eatup
integer*1 gtoken
integer*1 ptoken(MAXTOK),t,token(MAXTOK)
integer nlpar
nlpar = 0
loop
t = gtoken(token,MAXTOK)
if ((t .eq. SEMICOL) .or. (t .eq. NEWLINE)) then
exit
end if
if (t .eq. LBRACE) then
call pbstr(token)
exit
end if
if ((t .eq. RBRACE) .or. (t .eq. EOF)) then
call synerr('unexpected brace or EOF.')
call pbstr(token)
exit
end if
if (t .eq. COMMA) then
if (gtoken(ptoken,MAXTOK) .ne. NEWLINE) then
call pbstr(ptoken)
end if
else if (t .eq. LPAREN) then
nlpar = nlpar + 1
else if (t .eq. RPAREN) then
nlpar = nlpar - 1
end if
call outstr(token)
until (nlpar .lt. 0)
if (nlpar .ne. 0) then
call synerr('unbalanced parentheses.')
end if
return
end
doの終わりのコードは、dostat()で生成します。
dostat()のRatofor版は以下の通り。
# dostat.r4 -- generate code for end of do statement
subroutine dostat(lab)
integer lab
call outcon(lab)
call outcon(lab+1)
return
end
WATCOM Fortran77版は以下の通り。
c dostat.f -- generate code for end of do statement
subroutine dostat(lab)
integer lab
call outcon(lab)
call outcon(lab+1)
return
end
Ratforプリプロセッサー -- コード生成 "if"と"if-else" ― 2017年06月07日 15:41
"if"文に出会ったら、2つのラベル、LとL+1を生成し、
if(.not(条件)) goto L
を出力します。それは、ifcode()で行ない、ラベルを返します。
ifcode()のRatofor版は以下の通り。
# ifcode.r4 -- generate initial code for if
subroutine ifcode(lab)
integer lab
integer labgen
lab = labgen(2)
call ifgo(lab)
return
end
WATCOM Fortran77版は以下の通り。
c ifcode.f -- generate initial code for if
subroutine ifcode(lab)
integer lab
integer labgen
lab = labgen(2)
call ifgo(lab)
return
end
ifgo()は、
if(.not.(条件)) goto lab
を作り出します。
ifgo()のRatofor版は以下の通り。
# ifgo.r4 -- generate "if (.not. (...)) goto lab"
include ratfor.def
subroutine ifgo(lab)
integer lab
string ifnot "if (.not. "
call outtab # get to column 7
call outstr(ifnot) # "if (.not. "
call balpar # collect and output condition
call outch(RPAREN) # ")"
call outch(BLANK) # " "
call outgo(lab) # "goto lab"
return
end
WATCOM Fortran77版は以下の通り。
c ifgo.f -- generate "if (.not. (...)) goto lab"
include ratfor.def
subroutine ifgo(lab)
integer lab
integer*1 ifnot(11)
data ifnot(1)/LETi/
data ifnot(2)/LETf/
data ifnot(3)/BLANK/
data ifnot(4)/LPAREN/
data ifnot(5)/PERIOD/
data ifnot(6)/LETn/
data ifnot(7)/LETo/
data ifnot(8)/LETt/
data ifnot(9)/PERIOD/
data ifnot(10)/BLANK/
data ifnot(11)/EOS/
call outtab ! get to column 7
call outstr(ifnot) ! "if (.not. "
call balpar ! collect and output condition
call outch(RPAREN) ! ")"
call outch(BLANK) ! " "
call outgo(lab) ! "goto lab"
return
end
elseの開始、ifまたはelse ifの終了は、
goto L + 1
L continue
を作り出します。ここでLはスタックに積んであったラベルです。
elseif()のRatofor版は以下の通り。
# elseif.f -- generate code for end of if before else
subroutine elseif(lab)
integer lab
call outgo(lab+1)
call outcon(lab)
return
end
WATCOM Fortran77版は以下の通り。
c elseif.f -- generate code for end of if before else
subroutine elseif(lab)
integer lab
call outgo(lab+1)
call outcon(lab)
return
end
labgen()は、必要な数だけのラベルを生成します。ラベルは、50000からとします。
labgen()のRatofor版は以下の通り。
# labgen.r4 -- generates n consecutive labels, return first one
integer function labgen(n)
integer n
integer label
data label/50000/
labgen = label
label = label + n
return
end
WATCOM Fortran77版は以下の通り。
c labgen.f -- generates n consecutive labels, return first one
integer function labgen(n)
integer n
integer label
data label/50000/
labgen = label
label = label + n
return
end
balpar()は、条件を(複数行に渡っているのもまとめて)取り出し、出力します。 また、cnvop()で、演算子"<"、"<="、">"、">="、"=="、"!="、"&"、"|"を ".lt."、".le."、".gt."、".ge."、".eq."、".ne."、".and."、".or."に変換します。
balpar()のRatofor版は以下の通り。
# balpar.r4 -- copy balanced paren string
include ratfor.def
subroutine balpar
character gtoken
character t,token(MAXTOK)
integer nlpar
integer iindex
string opcode "=> 0)
call cnvop(token,opstr) # convert logical operator
call outstr(opstr)
else
call outstr(token)
} until (nlpar <= 0)
if (nlpar != 0)
call synerr('missing parenthesis in condition.')
return
end
WATCOM Fortran77版は以下の通り。
c balpar.f -- copy balanced paren string
include ratfor.def
subroutine balpar
integer*1 gtoken
integer*1 opstr(MAXTOK),t,token(MAXTOK)
integer nlpar
if (gtoken(token,MAXTOK) .ne. LPAREN) then
call synerr('missing left paren.')
return
end if
call outstr(token)
nlpar = 1
loop
t = gtoken(token,MAXTOK)
if ((t .eq. SEMICOL) .or. (t .eq. LBRACE) .or.
1 (t .eq. RBRACE) .or. (t .eq. EOF)) then
call pbstr(token)
exit
end if
if (t .eq. NEWLINE) then ! delete newlines
token(1) = EOS
else if (t .eq. LPAREN) then
nlpar = nlpar + 1
else if (t .eq. RPAREN) then
nlpar = nlpar - 1
! else nothing special
end if
if (islgop(token(1)) .eq. YES) then
call cnvop(token,opstr) ! convert logical operator
call outstr(opstr)
else
call outstr(token)
endif
until (nlpar .le. 0)
if (nlpar .ne. 0) then
call synerr('missing parenthesis in condition.')
end if
return
end
cnvop()のRatofor版は以下の通り。
# cnvop.r4 -- convert logical oprator to FORTRAN style string
include ratfor.def
subroutine cnvop(token,opstr)
integer*1 token(MAXTOK),opstr(MAXTOK)
integer*1 ntoken(MAXTOK),t
integer*1 gtoken
string opeq " .eq. "
string opne " .en. "
string opgt " .gt. "
string opge " .ge. "
string oplt " .lt. "
string ople " .le. "
string opnot " .not. "
string opand " .and. "
string opor " .or. "
t = gtoken(ntoken,MAXTOK)
if (t .eq. OPEQUAL) then
if (token(1) .eq. OPEQUAL) then
call scopy(opeq,1,opstr,1)
else if (token(1) .eq. OPGTHAN) then
call scopy(opge,1,opstr,1)
else if (token(1) .eq. OPLTHAN) then
call scopy(ople,1,opstr,1)
else if (token(1) .eq. OPNOT) then
call scopy(opne,1,opstr,1)
end if
else
call pbstr(ntoken)
if (token(1) .eq. OPGTHAN) then
call scopy(opgt,1,opstr,1)
else if (token(1) .eq. OPLTHAN) then
call scopy(oplt,1,opstr,1)
else if (token(1) .eq. OPNOT) then
call scopy(opnot,1,opstr,1)
else if (token(1) .eq. OPAND) then
call scopy(opand,1,opstr,1)
else if (token(1) .eq. OPOR) then
call scopy(opor,1,opstr,1)
end if
end if
return
end
WATCOM Fortran77版は以下の通り。
c cnvop.f -- convert logical oprator to FORTRAN style string
include ratfor.def
subroutine cnvop(token,opstr)
integer*1 token(MAXTOK),opstr(MAXTOK)
integer*1 ntoken(MAXTOK),t
integer*1 gtoken
integer*1 opeq(7)
data opeq(1)/BLANK/
data opeq(2)/PERIOD/
data opeq(3)/LETe/
data opeq(4)/LETq/
data opeq(5)/PERIOD/
data opeq(6)/BLANK/
data opeq(7)/EOS/
integer*1 opne(7)
data opne(1)/BLANK/
data opne(2)/PERIOD/
data opne(3)/LETn/
data opne(4)/LETe/
data opne(5)/PERIOD/
data opne(6)/BLANK/
data opne(7)/EOS/
integer*1 opgt(7)
data opgt(1)/BLANK/
data opgt(2)/PERIOD/
data opgt(3)/LETg/
data opgt(4)/LETt/
data opgt(5)/PERIOD/
data opgt(6)/BLANK/
data opgt(7)/EOS/
integer*1 opge(7)
data opge(1)/BLANK/
data opge(2)/PERIOD/
data opge(3)/LETg/
data opge(4)/LETe/
data opge(5)/PERIOD/
data opge(6)/BLANK/
data opge(7)/EOS/
integer*1 oplt(7)
data oplt(1)/BLANK/
data oplt(2)/PERIOD/
data oplt(3)/LETl/
data oplt(4)/LETt/
data oplt(5)/PERIOD/
data oplt(6)/BLANK/
data oplt(7)/EOS/
integer*1 ople(7)
data ople(1)/BLANK/
data ople(2)/PERIOD/
data ople(3)/LETl/
data ople(4)/LETe/
data ople(5)/PERIOD/
data ople(6)/BLANK/
data ople(7)/EOS/
integer*1 opnot(8)
data opnot(1)/BLANK/
data opnot(2)/PERIOD/
data opnot(3)/LETn/
data opnot(4)/LETo/
data opnot(5)/LETt/
data opnot(6)/PERIOD/
data opnot(7)/BLANK/
data opnot(8)/EOS/
integer*1 opand(8)
data opand(1)/BLANK/
data opand(2)/PERIOD/
data opand(3)/LETa/
data opand(4)/LETn/
data opand(5)/LETd/
data opand(6)/PERIOD/
data opand(7)/BLANK/
data opand(8)/EOS/
integer*1 opor(7)
data opor(1)/BLANK/
data opor(2)/PERIOD/
data opor(3)/LETo/
data opor(4)/LETr/
data opor(5)/PERIOD/
data opor(6)/BLANK/
data opor(7)/EOS/
t = gtoken(ntoken,MAXTOK)
if (t .eq. OPEQUAL) then
if (token(1) .eq. OPEQUAL) then
call scopy(opeq,1,opstr,1)
else if (token(1) .eq. OPGTHAN) then
call scopy(opge,1,opstr,1)
else if (token(1) .eq. OPLTHAN) then
call scopy(ople,1,opstr,1)
else if (token(1) .eq. OPNOT) then
call scopy(opne,1,opstr,1)
end if
else
call pbstr(ntoken)
if (token(1) .eq. OPGTHAN) then
call scopy(opgt,1,opstr,1)
else if (token(1) .eq. OPLTHAN) then
call scopy(oplt,1,opstr,1)
else if (token(1) .eq. OPNOT) then
call scopy(opnot,1,opstr,1)
else if (token(1) .eq. OPAND) then
call scopy(opand,1,opstr,1)
else if (token(1) .eq. OPOR) then
call scopy(opor,1,opstr,1)
end if
end if
return
end
ここまでに出てきた、下層の下請けルーチン、outtab()、outstr()、outch()、outgo()は、 以下の通りです。
outtab()のRatofor版は以下の通り。
# outtab.r4 -^- get past column 6
include ratfor.def
subroutine outtab
include coutln.ri
while (outp .lt. 6) do
call outch(BLANK)
end while
return
end
WATCOM Fortran77版は以下の通り。
c outtab.f -^- get past column 6
include ratfor.def
subroutine outtab
include coutln.fi
while (outp .lt. 6) do
call outch(BLANK)
end while
return
end
outstr()のRatofor版は以下の通り。
# outstr.r4 -- output string
include ratfor.def
subroutine outstr(str)
character str(ARB)
integer i,j
character c
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
WATCOM Fortran77版は以下の通り。
c outstr.f -- output string
include ratfor.def
subroutine outstr(str)
integer*1 str(ARB)
integer i,j
integer*1 c
i = 1
while (str(i) .ne. EOS) do
c = str(i)
if ((c .ne. SQUOTE) .and. (c .ne. DQUOTE)) then
call outch(c)
else
i = i + 1
j = i ! find end
while (str(j) .ne. c) do
j = j + 1
end while
call outnum(j-i)
call outch(LETH)
while (i .lt. j) do
call outch(str(i))
i = i + 1
end while
end if
i = i + 1
end while
return
end
outch()のRatofor版は以下の通り。
# outch.r4 -- put one character into output buffer
include ratfor.def
subroutine outch(c)
character c
integer i
include coutln.ri
if (outp >= 72 ) # continution 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
WATCOM Fortran77版は以下の通り。
c outch.f -- put one character into output buffer
include ratfor.def
subroutine outch(c)
integer*1 c
integer i
include coutln.fi
if (outp .ge. 72 ) then ! continution card
call outdon
i = 1
while (i .lt. 6) do
outbuf(i) = BLANK
i = i + 1
end while
outbuf(6) = STAR
outp = 6
end if
outp = outp + 1
outbuf(outp) = c
return
end
outgo()のRatofor版は以下の通り。
# outgo.r4 -- output "goto n"
include ratfor.def
subroutine outgo(n)
integer n
string(goto,"goto")
call outtab
call outstr(goto)
call outch(BLANK)
call outnum(n)
call outdon
return
end
WATCOM Fortran77版は以下の通り。
c outgo.f -- output "goto n"
include ratfor.def
subroutine outgo(n)
integer n
string(goto,"goto")
call outtab
call outstr(goto)
call outch(BLANK)
call outnum(n)
call outdon
return
end
Ratforプリプロセッサー -- 構文解析 ― 2017年05月25日 16:50
Ratforで書かれたプログラム文は、parse()で解析され、lex()が返す綴り別に それがコードの始まりの時は、コード生成ルーチンを呼び出し、スタックに綴りの種類と 名札が積まれる。コードの終わりの時は、unstak()が呼ばれスタックから取りおろしをします。 綴りは、defineやmacroのように、initkw()でテーブルにセットします。
parse()のRatofor版は以下の通り。
# parse.r4 -- parse Ratfor source program
include ratfor.def
subroutine parse
character lexstr(MAXTOK)
character lex, token
integer lab,sp
integer labval(MAXSTACK)
character lextyp(MAXSTACK)
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 == LEXFOR)
call forcod(lab)
else if (token == LEXREPEAT)
call repcod(lab)
else if (token == LEXUNTIL) {
if (lextyp(sp) == LEXREPEAT)
call synerr('illegal until.')
}
else if (token == LEXSTRING)
call strngc
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
| token == LEXFOR | token == LEXREPEAT) {
sp = sp + 1
if (sp > MAXSTACK)
call synerr('stack overflow in paser.')
lextyp(sp) = token
labval(sp) = lab
}
else }
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
WATCOM Fortran77版は以下の通り。
c parse.f -- parse Ratfor source program
include ratfor.def
subroutine parse
integer*1 lexstr(MAXTOK)
integer*1 lex,lextyp(MAXSTACK),token
integer lab,labval(MAXSTACK),sp
call initkw ! install keywords in table
sp = 1
lextyp(1) = EOF
token = lex(lexstr)
while (token .ne. EOF) do
if (token .eq. LEXIF) then
call ifcode(lab)
else if (token .eq. LEXDO) then
call docode(lab)
else if (token .eq. LEXWHILE) then
call whilec(lab)
else if (token .eq. LEXFOR) then
call forcod(lab)
else if (token .eq. LEXREPEAT) then
call repcod(lab)
else if (token .eq. LEXUNTIL) then
if (lextyp(sp) .ne. LEXREPEAT) then
call synerr('illegal until.')
end if
else if (token .eq. LEXSTRING) then
call strngc
else if (token .eq. LEXDIGITS) then
call labelc(lexstr)
else if (token .eq. LEXELSE) then
if (lextyp(sp) .eq. LEXIF) then
call elseif(labval(sp))
else
call synerr('illegal else.')
end if
end if
if ((token .eq. LEXIF) .or. (token .eq. LEXELSE) .or.
1 (token .eq. LEXWHILE) .or. (token .eq. LEXDO) .or.
2 (token .eq. LEXDIGITS) .or. (token .eq. LBRACE) .or.
3 (token .eq. LEXFOR) .or. (token .eq. LEXREPEAT)) then
sp = sp + 1
if (sp .gt. MAXSTACK) then
call synerr('stack overflow in paser.')
end if
lextyp(sp) = token
labval(sp) = lab
else
if (token .eq. RBRACE) then
if (lextyp(sp) .eq. LBRACE) then
sp = sp - 1
else
call synerr('illegal right brace.')
end if
else if (token .eq. LEXOTHER) then
call otherc(lexstr)
else if (token .eq. LEXBREAK .or. token .eq. LEXNEXT) then
call brknxt(sp,lextyp,labval,token)
end if
token = lex(lexstr) ! peek at next token
call pbstr(lexstr)
call unstak(sp,lextyp,labval,token)
end if
token = lex(lexstr)
end while
if (sp .ne. 1) then
call synerr('unexpected EOF.')
end if
return
end
initkw()は、見出し語をinstal()を使って登録します。
Ratofor版は以下の通り。
# initkw.f -- install Ratfor keyword
include ratfor.def
subroutine initkw
string(ifst,"if")
character iftyp(2)
data iftyp(1)/LEXIF/
data iftyp(2)/EOS/
string(elsest,"else")
character elstyp(2)
data elstyp(1)/LEXELSE/
data elstyp(2)/EOS/
string(whilst,"while")
character whityp(2)
data whityp(1)/LEXWHILE/
data whityp(2)/EOS/
string(forst,"for")
character fortyp(2)
data fortyp(1)/LEXFOR/
data fortyp(2)/EOS/
string(repest,"repeat")
character reptyp(2)
data reptyp(1)/LEXREPEAT/
data reptyp(2)/EOS/
string(untist,"until")
character unttyp(2)
data unttyp(1)/LEXUNTIL/
data unttyp(2)/EOS/
string(dost,"do")
character dotyp(2)
data dotyp(1)/LEXDO/
data dotyp(2)/EOS/
string(breast,"break")
character bretyp(2)
data bretyp(1)/LEXBREAK/
data bretyp(2)/EOS/
string(nextst,"next")
character nextyp(2)
data nextyp(1)/LEXNEXT/
data nextyp(2)/EOS/
integer*1 strtyp(2)
data strtyp(1)/LEXSTRING/
data strtyp(2)/EOS/
call instal(ifst,iftyp)
call instal(elsest,elstyp)
call instal(whilst,whityp)
call instal(forst,fortyp)
call instal(repest,reptyp)
call instal(untist,unttyp)
call instal(dost,dotyp)
call instal(breast,bretyp)
call instal(nextst,nextyp)
call instal(strst,strtyp)
return
end
WATCOM Fortran77版は以下の通り。
c initkw.f -- install Ratfor keyword
include ratfor.def
subroutine initkw
string(ifst,"if")
integer*1 iftyp(2)
data iftyp(1)/LEXIF/
data iftyp(2)/EOS/
string(elsest,"else")
integer*1 elstyp(2)
data elstyp(1)/LEXELSE/
data elstyp(2)/EOS/
string(whilst,"while")
integer*1 whityp(2)
data whityp(1)/LEXWHILE/
data whityp(2)/EOS/
string(forst,"for")
integer*1 fortyp(2)
data fortyp(1)/LEXFOR/
data fortyp(2)/EOS/
string(repest,"repeat")
integer*1 reptyp(2)
data reptyp(1)/LEXREPEAT/
data reptyp(2)/EOS/
string(untist,"until")
integer*1 unttyp(2)
data unttyp(1)/LEXUNTIL/
data unttyp(2)/EOS/
string(dost,"do")
integer*1 dotyp(2)
data dotyp(1)/LEXDO/
data dotyp(2)/EOS/
string(breast,"break")
integer*1 bretyp(2)
data bretyp(1)/LEXBREAK/
data bretyp(2)/EOS/
string(nextst,"next")
integer*1 nextyp(2)
data nextyp(1)/LEXNEXT/
data nextyp(2)/EOS/
integer*1 strst(7)
data strst(1)/LETs/
data strst(2)/LETt/
data strst(3)/LETr/
data strst(4)/LETi/
data strst(5)/LETn/
data strst(6)/LETg/
data strst(7)/EOS/
integer*1 strtyp(2)
data strtyp(1)/LEXSTRING/
data strtyp(2)/EOS/
call instal(ifst,iftyp)
call instal(elsest,elstyp)
call instal(whilst,whityp)
call instal(forst,fortyp)
call instal(repest,reptyp)
call instal(untist,unttyp)
call instal(dost,dotyp)
call instal(breast,bretyp)
call instal(nextst,nextyp)
call instal(strst,strtyp)
return
end
unstak()は、スタックに最後に積まれた綴りに応じた構文の終わりのコードを生成し、 スタックから取り下ろします。
unstak()のRatofor版は以下の通り。
# unstack.f -- unstack at end of statment
include ratfor.def
subroutine unstak(sp,lextyp,labval,token)
integer sp
integer labval(MAXSTACK)
character token
character lextyp(MAXSTACK)
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) == LEXFOR)
call forsta(labval(sp))
else if (lextyp(sp) == LEXREPEAT)
call repats(labval(sp))
else if (lextyp(sp) == LEXWHILE)
call whiles(labval(sp))
}
return
end
WATCOM Fortran77版は以下の通り。
c unstack.f -- unstack at end of statment
include ratfor.def
subroutine unstak(sp,lextyp,labval,token)
integer labval(MAXSTACK),sp
integer*1 lextyp(MAXSTACK),token
while (sp .gt. 1) do
if (lextyp(sp) .eq. LBRACE) then
exit
end if
if ((lextyp(sp) .eq. LEXIF) .and. (token .eq. LEXELSE)) then
exit
end if
if (lextyp(sp) .eq. LEXIF) then
call outcon(labval(sp))
else if (lextyp(sp) .eq. LEXELSE) then
if (sp .gt. 2) then
sp = sp - 1
end if
call outcon(labval(sp)+1)
else if (lextyp(sp) .eq. LEXDO) then
call dostat(labval(sp))
else if (lextyp(sp) .eq. LEXFOR) then
call forsta(labval(sp))
else if (lextyp(sp) .eq. LEXREPEAT) then
call repats(labval(sp))
else if (lextyp(sp) .eq. LEXWHILE) then
call whiles(labval(sp))
end if
sp = sp - 1
end while
return
end
Ratforのメインプリグラムはとても簡単です。
Ratofor版は以下の通り。
# ratfor.r4 -- main program for Ratfor
program ratfor
call initfile
call parse
stop
end
WATCOM Fortran77版は以下の通り。
c ratfor.f -- main program for Ratfor
program ratfor
call initfile
call parse
stop
end
Ratforプリプロセッサー -- 字句解析 ― 2017年05月13日 15:55
Ratforで書かれたプログラムからtokenを切り出すのは、defineやmacroと同様であるが、 引用符の通り扱いに注意が必要である。引用符は、ほとんどの場合ペアで使われ、その間に NEWLINEが入ることはない。この点に配慮する必要がある。
Ratofor版は以下の通り。
# gtoken.f -- get token for ratfor
include ratfor.def
character function gtoken(lexstr,toksiz)
integer toksiz
character lexstr(toksiz)
character ngetc,type
integer i
character c
include cline.ri
while (ngetc(c) != EOF)
if ((c != BLANK) & (c != TAB))
break
call putbak(c)
for(i = 1;i < toksiz-1;i = i +1) {
gtoken = type(ngetc(lexstr(i)))
if ((gtoken != LETTER) & (gtoken != DIGIT))
break
}
if (i >= toksiz-1)
call synerr('token too long.')
if (i > 1)
call putbak(lexstr(i))
lexstr(i) = EOS
gtoken = ALPHA
else if ((lexstr(1) == SQUOTE) | (lexstr(1) == DQUOTE))
for (i = 2;ngetc(lexstr(i)) != lexstr(1)) {
if (lexstr(i) == NEWLINE) {
call synerr('missing quote.')
lexstr(i) = lexstr(1)
call putbak(NEWLINE)
break
}
}
else if (lexstr(1) == SHARP) { # strip comments
while (ngetc(lexstr(1)) != NEWLINE)
;
gtoken = NEWLINE
}
lexstr(i+1) = EOS
if (lexstr(1) == NEWLINE)
linect = linect + 1
return
end
WATCOM Fortran77版は以下の通り。
c gtoken.f -- get token for ratfor
include ratfor.def
integer*1 function gtoken(lexstr,toksiz)
integer toksiz
integer*1 lexstr(toksiz)
integer*1 ngetc,type
integer i
integer*1 c
include cline.fi
while (ngetc(c) .ne. EOF) do
if ((c .ne. BLANK) .and. (c .ne. TAB)) then
exit
end if
end while
call putbak(c)
i = 1
while (i .lt. toksiz-1) do
gtoken = type(ngetc(lexstr(i)))
if ((gtoken .ne. LETTER) .and. (gtoken .ne. DIGIT)) then
exit
end if
i = i + 1
end while
if (i .ge. toksiz-1) then
call synerr('token too long.')
end if
if (i .gt. 1) then
call putbak(lexstr(i))
lexstr(i) = EOS
gtoken = ALPHA
else if (lexstr(1) .eq. SQUOTE .or. lexstr(1) .eq. DQUOTE) then
i = 2
while (ngetc(lexstr(i)) .ne. lexstr(1)) do
if (lexstr(i) .eq. NEWLINE) then
call synerr('missing quote.')
lexstr(i) = lexstr(1)
call putbak(NEWLINE)
exit
end if
i = i + 1
end while
else if (lexstr(1) .eq. SHARP) then ! strip comments
while (ngetc(lexstr(1)) .ne. NEWLINE) do
! nothing to do
end while
gtoken = NEWLINE
end if
lexstr(i+1) = EOS
if (lexstr(1) .eq. NEWLINE) then
linect = linect + 1
end if
return
end
gtoken()の下請けルーチンsynerr()はsyntax errorを出力する。errorは、行番号とともに出力されるが、 行番号linectは、必要なモジュールで共有できるように、共通領域clineにおいている。
synerr()のRatofor版は以下の通り。
# synerr -- report Ratfor syntax error
include ratfor.def
subroutine synerr(msg)
character msg(MAXLINE)
character lc(MAXLINE)
integer itoc
integer junk
include cline.ri
call remark('error at line.')
junk = itoc(linect,lc,MAXLINE)
call putlin(lc,ERROUT)
call fputc(ERROUT,COLON)
call remark(msg)
return
end
WATCOM Fortran77版は以下の通り。
c synerr -- report Ratfor syntax error
include ratfor.def
subroutine synerr(msg)
character msg(MAXLINE)
integer*1 lc(MAXLINE)
integer itoc
integer junk
include cline.fi
call remark('error at line.')
junk = itoc(linect,lc,MAXLINE)
call putlin(lc,ERROUT)
call fputc(ERROUT,BLANK)
call remark(msg)
return
end
clineのRatofor版は以下の通り。
# cline.ri
common /cline/linect
integer linect
data linect/1/
WATCOM Fortran77版は以下の通り。
c cline.fi
common /cline/linect
integer linect
data linect/1/
gtoken()で切り出されたtokenは、次のlex()に引き継がれ解析される。 ここでも字句の判定に、lookup()を使用する。
lex()のRatfor版は以下の通り。
# lex.r4- return lexical type of token
include ratfor.def
character function lex(lexstr)
character lexstr(MAXTOK)
character gtoken
integer alldig,lookup
character ltype(2)
while (gtoken(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
WATCOM Fortran77版は以下の通り。
c lex.f- return lexical type of token
include ratfor.def
integer function lex(lexstr)
integer*1 lexstr(MAXTOK)
integer*1 gtoken
integer alldig,lookup
integer*1 ltype(2)
while (gtoken(lexstr,MAXTOK) .eq. NEWLINE) do
! nothing to do
end while
lex = lexstr(1)
if ((lex .eq. EOF) .or. (lex .eq. SEMICOL) .or.
1 (lex .eq. LBRACE) .or. (lex .eq. RBRACE)) then
return
end if
if (alldig(lexstr)) then
lex = LEXDIGITS
else if(lookup(lexstr,ltype) .eq. YES) then
lex = ltype(1)
else
lex = LEXOTHER
end if
return
end
lex()の下請けルーチンalldig()は、tokenが数字列か否かを判定する。
alldig()のRatfor版は以下の通り。
# alldig.r4 -- return YES if str is all digis
include ratfor.def
integer function alldig(s)
character s(ARB)
character type
character i
alldig = NO
if (s(1) == EOS)
return
for (i = 1; s(i) != EOS; i = i +1)
if (type(s(i)) != DIGIT)
return
alldig = YES
return
end
WATCOM Fortran77版は以下の通り。
c alldig -- return YES if str is all digis
include ratfor.def
integer function alldig(s)
integer*1 s(ARB)
integer*1 type
integer i
alldig = NO
if (s(1) .eq. EOS) then
return
end if
i = 1
while (s(i) .ne. EOS) do
if (type(s(i)) .ne. DIGIT) then
return
end if
i = i + 1
end while
alldig = YES
return
end
Ratforプリプロセッサー -- RatforからFortranへの変換 ― 2017年04月29日 09:51
ここまで、Ratforで書かれたプログラムを手作業でWATCOM Fortran 77でコンパイル できるよう修正してきた。これから、RatforのコードからFortran IVのレベルのコードに変換する プリプロセッサーを紹介する。
これまでに作成したツールとプリプロセッサーを使って、 Ratforで書かれたプログラムを実行可能なプログラムにする手順は、 以下のようになる。
,--------------,
| Ratfor |
| 原プログラム |
'--------------'
|
V .------------.
[include]<------| Ratfor.def |
| '------------'
V
[macro]
|
V
[ratfor]
|
V
[Watcom Fortran77]
|
V
.------------.
| 実行可能な |
{ プログラム |
'------------'
ところで、Ratfor言語の定義をBNFで記述すると以下のようになる。
プログラム : 文
| プログラム 文
文 : if ( 条件 ) 文
| if ( 条件 ) 文 else 文
| while ( 条件 ) 文
| for ( 初期設定; 条件; 再設定 ) 文
| repeat 文
| repeat 文 until ( 条件 )
| do 限界指定部 文
| 数字の列 文
| break
| next
| { プログラム }
| その他
Ratfor言語は、10種類程度の文からなっており、"その他"とはRatforが知らない文でFortranの 文が該当する。Ratforは、"if"や"while" などの制御構造を持った文をFortranの"if","go to","continue"などを 使って、制御構造を作りだすのである。
文の変換規則(コード変換規則)は、以下のようになる。
"if" 文
if ( 条件 ) 文
は、
if (.not. ( 条件 )) goto L
文
L continue
"if--else" 文
if ( 条件 ) 文1 else 文2
は、
if (.not. ( 条件 )) goto L
文1
go to L1
L continue
文2
L1 continue
"while" 文
while ( 条件 ) 文
は、
continue
L if (.not. ( 条件 )) go to L1
文
go to L
L1 continue
"for" 文
for ( 初期設定; 条件; 再設定 ) 文
は、
continue
初期設定
L if (.not. ( 条件 )) goto L2
文
L1 continue
再設定
goto L
L2 continue
"repeat" 文
repeat 文
は、
continue
L continue
文
L1 continue
go to L
L2 continue
"repeat-until" 文
continue
L continue
文
L1 continue
if (.not. ( 条件 )) go to L
L2 continue
"do" 文
do 限界指定部 文
は、
do L 限界指定部
文
L1 continue
L continue
"break"文は、"while"、"for"、"repeat"、"repeat-until"、"do"のループから抜け出すのに使用できる。 すなわち、ループの次の文に制御が移る。
"next"文は、ループの残りをジャンプして次の繰り返しに制御を移す。 "while"、"repeat-until"、"do"の各ループでは、条件判定部に、 "for"では、再設定に、"repeat"では、ループ本体の先頭に、それぞれ制御が移る。
論理式に使う演算子"<"や"&"等は、変換される。
Ratforの演算子 Fortranの演算子
-------------- ---------------
> .gt.
>= .ge.
< .lt.
<= .le.
== .eq.
!= .ne.
! .not.
& .and.
| .or.
引数付きマクロ処理 -- 機能改善 ― 2017年04月21日 13:54
完成した"macro"を使ってみて、以下の不具合が見つかった。
- マクロの外で、'や"で囲まれた文字列(たいていの場合文字列定数)に含まれるマクロ名も置換されてしまう。
- マクロ"string"は、英数字しか展開できない。
- マクロ"string"の下請けマクロ"len","str"が、プログラム中の変数とぶつかる可能性が多々ある。
このうち2に関しては、Ratforプリプロセッサーに組み込むこととし、 1は、macroのメインルーチンを手直しして対応し、3は、マクロ名を手直しして対応する。
macroの、メインルーチンの修正点は、マクロの外を処理している最中に "や'が出現したら、対応する"や'が出現するまで読み込み、出力する。ただし、 対応する"や'が出現しない場合は、先頭の"だけを出力する。
RATFOR版は、以下の通り。
# macro.r4 -- expand macros with arguments
program macro
character gettok
character defn(MAXDEF),t,token(MAXTOK)
integer lookup,push
integer ap,argstk(ARGSIZE),callst(CALLSIZE),i,nlb,plev(CALLSIZE)
string balp "()"
string defnam "define"
character deftyp(2)
data deftyp(1)/DEFTYPE/,deftyp(2)/EOS/
string incnam "incr"
character inctype(2)
data inctyp(1)/INCTYPE/,inctyp(2)/EOS/
string subnam "substr"
character subtype(2)
data subtyp(1)/SUBTYPE/,subtyp(2)/EOS/
string ifnam "ifelse"
character iftype(2)
data iftyp(1)/IFTYPE/,iftyp(2)/EOS/
string udfnam "ifelse"
character udftype(2)
data udftyp(1)/UDFTYPE/,udftyp(2)/EOS/
string ifdnam "ifdef"
character ifdtype(2)
data ifdtyp(1)/IFDTYPE/,ifdtyp(2)/EOS/
include cmacro.fi
call initfile
call inittbl
call initbuf
call instal(defnam,deftyp)
call instal(incnam,inctyp)
call instal(subnam,subtyp)
call instal(ifnam,iftyp)
call instal(udfnam,udftyp)
call instal(ifdnam,ifdtyp)
cp = 0
ap = 1
ep = 1
for (t = gettok(token,MAXTOK); t != EOF; t = gettok(token,MAXTOK)) {
if (t == ALPHA) {
if (lookup(token,defn) == NO)
call puttok(token)
else { # defined; put it in eval stack
cp = cp + 1
if (cp > CALLSIZE) then
call error('call stack overflow.')
callst(cp) = ap
ap = push(ep,argstk,ap)
call puttok(defn) # stack definition
call putchr(EOS)
ap = push(ep,argstk,ap)
call puttok(token) # stack name
call putchr(EOS)
ap = push(ep,argstk,ap)
t = gettok(token,MAXTOK) # peek at next
call pbstr(token)
if (t != LPAREN) # add ( ) if not present
call pbstr(balp)
plev(cp) = 0
}
}
else if (t == LBRACK) { # strip one level of [ ]
nlb = 1
repeat {
t = gettok(token,MAXTOK)
if (t == LBRACK)
nlb = nlb + 1
else if (t == RBRACK) {
nlb = nlb - 1
if (nlb == 0)
break
}
else if (t == EOF)
call error('EOF in string.')
call puttok(token)
}
else if (cp == 0) { # not in a macro at all
if (token(1) == SQUOTE ! token(1) == DQUOTE) {
for (i = 2; ngetc(token(i)) != token(1); i = i + 1) {
if (token(i) == NEWLINE) {
token(i+1) = EOS
call pbstr(token(2))
i = 1
break
}
}
token(i+1) = EOS
}
call puttok(token)
}
else if (t == LPAREN)
if (plev(cp) > 0)
call puttok(token)
plev(cp) = plev(cp) + 1
else if (t == RPAREN) {
plev(cp) = plev(cp) - 1
if (plev(cp) .gt. 0)
call puttok(token)
else { # end of argument list
call putchr(EOS)
call eval(argstk,callst(cp),ap-1)
ap = callst(cp) # pop eval stack
ep = argstk(ap)
cp = cp - 1
}
else if ((t == COMMA) $ (plev(cp) == 1)) {
call putchr(EOS)
ap = push(ep,argstk,ap)
}
else
call puttok(token)
}
if (cp != 0)
call error('unexpected EOF.')
stop
end
WATCOM Fortran 77版は以下の通り。
c macro.f -- expand macros with arguments
include ratfor.def
program macro
integer*1 gettok,ngetc
integer*1 defn(MAXDEF),t,token(MAXTOK)
integer lookup,push
integer ap,argstk(ARGSIZE),callst(CALLSIZE),i,nlb,plev(CALLSIZE)
integer*1 balp(3)
data balp(1)/LPAREN/
data balp(2)/RPAREN/
data balp(3)/EOS/
integer*1 defnam(7)
data defnam(1)/LETd/
data defnam(2)/LETe/
data defnam(3)/LETf/
data defnam(4)/LETi/
data defnam(5)/LETn/
data defnam(6)/LETe/
data defnam(7)/EOS/
integer*1 deftyp(2)
data deftyp(1)/DEFTYPE/
data deftyp(2)/EOS/
integer*1 incnam(5)
data incnam(1)/LETi/
data incnam(2)/LETn/
data incnam(3)/LETc/
data incnam(4)/LETr/
data incnam(5)/EOS/
integer*1 inctyp(2)
data inctyp(1)/INCTYPE/
data inctyp(2)/EOS/
integer*1 subnam(7)
data subnam(1)/LETs/
data subnam(2)/LETu/
data subnam(3)/LETb/
data subnam(4)/LETs/
data subnam(5)/LETt/
data subnam(6)/LETr/
data subnam(7)/EOS/
integer*1 subtyp(2)
data subtyp(1)/SUBTYPE/
data subtyp(2)/EOS/
integer*1 ifnam(7)
data ifnam(1)/LETi/
data ifnam(2)/LETf/
data ifnam(3)/LETe/
data ifnam(4)/LETl/
data ifnam(5)/LETs/
data ifnam(6)/LETe/
data ifnam(7)/EOS/
integer*1 iftyp(2)
data iftyp(1)/IFTYPE/
data iftyp(2)/EOS/
integer*1 udfnam(6)
data udfnam(1)/LETu/
data udfnam(2)/LETn/
data udfnam(3)/LETd/
data udfnam(4)/LETe/
data udfnam(5)/LETf/
data udfnam(6)/EOS/
integer*1 udftyp(2)
data udftyp(1)/UDFTYPE/
data udftyp(2)/EOS/
integer*1 ifdnam(6)
data ifdnam(1)/LETi/
data ifdnam(2)/LETf/
data ifdnam(3)/LETd/
data ifdnam(4)/LETe/
data ifdnam(5)/LETf/
data ifdnam(6)/EOS/
integer*1 ifdtyp(2)
data ifdtyp(1)/IFDTYPE/
data ifdtyp(2)/EOS/
include cmacro.fi
call initfile
call inittbl
call initbuf
call instal(defnam,deftyp)
call instal(incnam,inctyp)
call instal(subnam,subtyp)
call instal(ifnam,iftyp)
call instal(udfnam,udftyp)
call instal(ifdnam,ifdtyp)
cp = 0 ! current call stack pointer
ap = 1 ! next free position in argstk
ep = 1 ! next free position in evalst
t = gettok(token,MAXTOK)
while (t .ne. EOF) do
if (t .eq. ALPHA) then
if (lookup(token,defn) .eq. NO) then
call puttok(token)
else ! defined; put it in eval stack
cp = cp + 1
if (cp .gt. CALLSIZE) then
call error('call stack overflow.')
end if
callst(cp) = ap
ap = push(ep,argstk,ap)
call puttok(defn) ! stack definition
call putchr(EOS)
ap = push(ep,argstk,ap)
call puttok(token) ! stack name
call putchr(EOS)
ap = push(ep,argstk,ap)
t = gettok(token,MAXTOK) ! peek at next
call pbstr(token)
if (t .ne. LPAREN) then ! add ( ) if not present
call pbstr(balp)
end if
plev(cp) = 0
end if
else if (t .eq. LBRACK) then ! strip one level of [ ]
nlb = 1
loop
t = gettok(token,MAXTOK)
if (t .eq. LBRACK) then
nlb = nlb + 1
else if (t .eq. RBRACK) then
nlb = nlb - 1
if (nlb .eq. 0) then
exit
end if
else if (t .eq. EOF) then
call error('EOF in string.')
end if
call puttok(token)
end loop
else if (cp .eq. 0) then ! not in a macro at all
if (token(1) .eq. SQUOTE .or. token(1) .eq. DQUOTE) then
i = 2
while (ngetc(token(i)) .ne. token(1)) do
if (token(i) .eq. NEWLINE) then
token(i+1) = EOS
call pbstr(token(2))
i = 1
exit
end if
i = i + 1
end while
token(i+1) = EOS
end if
call puttok(token)
else if (t .eq. LPAREN) then
if (plev(cp) .gt. 0) then
call puttok(token)
end if
plev(cp) = plev(cp) + 1
else if (t .eq. RPAREN) then
plev(cp) = plev(cp) - 1
if (plev(cp) .gt. 0) then
call puttok(token)
else ! end of argument list
call putchr(EOS)
call eval(argstk,callst(cp),ap-1)
ap = callst(cp) ! pop eval stack
ep = argstk(ap)
cp = cp - 1
end if
else if ((t .eq. COMMA) .and. (plev(cp) .eq. 1)) then
call putchr(EOS)
ap = push(ep,argstk,ap)
else
call puttok(token)
end if
t = gettok(token,MAXTOK)
end while
if (cp .ne. 0) then
call error('unexpected EOF.')
end if
stop
end
マクロ"string"の下請けマクロ"len","str"は、以下のように変更した。
define(00length00,[ifelse($1,,0,[incr(00length00(substr($1,2)))])])
define(string,[integer*1 $1(00length00(substr($2,2)))
00string00($1,substr($2,2),0)
data $1(00length00(substr($2,2)))/EOS/
])
define(00string00,[ifelse($2,",, data $1(incr($3))/[LET]substr($2,1,1)/
[00string00($1,substr($2,2),incr($3))])])
引数付きマクロ処理 -- 機能拡張(2) ― 2017年04月14日 19:53
組み込み関数"ifelse"の実装は以下の通り。
RATFOR版
# doif.f -- select one of two arguments
include ratfor.def
subroutine doif(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer equal
integer a2,a3,a4,a5
include cmacro.ri
if (j-i < 5)
return
a2 = argstk(i + 2)
a3 = argstk(i + 3)
a4 = argstk(i + 4)
a5 = argstk(i + 5)
if (equal(evalst(a2),evalst(a3)) == YES)
call pbstr(evalst(a4))
else
call pbstr(evalst(a5))
return
end
WATCOM Fortran 77版は以下の通り。
c doif.f -- select one of two arguments
include ratfor.def
subroutine doif(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer equal
integer a2,a3,a4,a5
include cmacro.fi
if (j-i .lt. 5) then
return
end if
a2 = argstk(i + 2)
a3 = argstk(i + 3)
a4 = argstk(i + 4)
a5 = argstk(i + 5)
if (equal(evalst(a2),evalst(a3)) .eq. YES) then
call pbstr(evalst(a4))
else
call pbstr(evalst(a5))
end if
return
end
組み込み関数"incr"は、引数を数値化して、下請けルーチンpbnum()で+1し、文字列化して入力に戻す。"incr"の実装は 以下の通り。
RATFOR版
# doinc.r4 -- increment argument by 1
include ratfor.def
subroutine doincr(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer ctoi
integer k
include cmacro.ri
k = argstk(i+2)
call pbnum(ctoi(evalst,k)+1)
return
end
WATCOM Fortran 77版は以下の通り。
c doinc.f -- increment argument by 1
include ratfor.def
subroutine doinc(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer ctoi
integer k
include cmacro.fi
k = argstk(i+2)
call pbnum(ctoi(evalst,k)+1)
return
end
下請けルーチンpbnum()は以下の通り。
RATFOR版は以下の通り。
# pbnum.r4 -- convert number to string, push back on input
include ratfor.def
subroutine pbnum(n)
integer n
integer mod
integer m,num
string digits "0123456789"
num = n
repeat {
m = mod(num,10)
call putbak(digits(m+1))
num = num/10
} until (num == 0)
return
end
WATCOM Fortran 77版は以下の通り。
c pbnum.f -- convert number to string, push back on input
include ratfor.def
subroutine pbnum(n)
integer n
integer mod
integer m,num
integer*1 digits(11)
data digits(1)/LET0/
data digits(2)/LET1/
data digits(3)/LET2/
data digits(4)/LET3/
data digits(5)/LET4/
data digits(6)/LET5/
data digits(7)/LET6/
data digits(8)/LET7/
data digits(9)/LET8/
data digits(10)/LET9/
data digits(11)/EOS/
num = n
loop
m = mod(num,10)
call putbak(digits(m+1))
num = num/10
until (num .eq. 0)
return
end
組み込み関数"substr"の実装は、以下の通り。
RATFOR版は以下の通り。
# dosub.r4 -- select substring
include ratfor.def
subroutine dosub(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer ctoi,length,min
integer ap,fc,k,nc
include cmacro.ri
if (j-i < 3)
return
if (j-i < 4)
nc = MAXTOK
else {
k = argstk(i+4)
nc = ctoi(evalst,k) # number of characters
}
k = argstk(i+3) # origin
ap = argstk(i+2) # target string
fc = ap + ctoi(evalst,k) - 1 # first char of substring
if ((fc >= ap) & (fc < ap+length(evalst(ap)))) { # subarrays
k = fc + min(nc,length(evalst(fc))) - 1
for ( ; k >= fc ; k = k -1)
call putbak(evalst(k))
}
return
end
WATCOM Fortran 77版は以下の通り。
c dosub.f -- select substring
include ratfor.def
subroutine dosub(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer ctoi,length,min
integer ap,fc,k,nc
include cmacro.fi
if (j-i .lt. 3) then
return
end if
if (j-i .lt. 4) then
nc = MAXTOK
else
k = argstk(i+4)
nc = ctoi(evalst,k) ! number of characters
end if
k = argstk(i+3) ! origin
ap = argstk(i+2) ! target string
fc = ap + ctoi(evalst,k) - 1 ! first char of substring
if ((fc .ge. ap) .and. (fc .lt. ap+length(evalst(ap)))) then ! subarrays
k = fc + min(nc,length(evalst(fc))) - 1
while (k .ge. fc) do
call putbak(evalst(k))
k = k - 1
end while
end if
return
end
組み込み関数"undef"の実装は、以下の通り。
RATFOR版
# doudf.f -- undefine macro
include ratfor.def
subroutine doudf(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer a2
include cmacro.r4
if (j-i .lt. 1)
return
a2 = argstk(i + 2)
call uninst(evalst(a2))
return
end
WATCOM Fortran 77版は以下の通り。
c doudf.f -- undefine macro
include ratfor.def
subroutine doudf(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer a2
include cmacro.fi
if (j-i .lt. 1) then
return
end if
a2 = argstk(i + 2)
call uninst(evalst(a2))
return
end
組み込み関数"ifdef"の実装は、以下の通り。
RATFOR版
# doifd.r4 -- define if macro is defined
include ratfor.def
subroutine doifd(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer lookup
integer a2,a3,a4
character junk(MAXDEF)
include cmacro.fi
if (j-i .lt. 4)
return
a2 = argstk(i + 2)
a3 = argstk(i + 3)
a4 = argstk(i + 4)
if (lookup(evalst(a2),junk) .eq. YES)
call pbstr(evalst(a3))
else
call pbstr(evalst(a4))
return
end
WATCOM Fortran 77版は以下の通り。
# doifd.r4 -- define if macro is defined
include ratfor.def
subroutine doifd(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer lookup
integer a2,a3,a4
character junk(MAXDEF)
include cmacro.fi
if (j-i .lt. 4)
return
a2 = argstk(i + 2)
a3 = argstk(i + 3)
a4 = argstk(i + 4)
if (lookup(evalst(a2),junk) .eq. YES)
call pbstr(evalst(a3))
else
call pbstr(evalst(a4))
return
end
マクロの応用を紹介する。
文字列の長さを返す"len"。再帰的定義になっているが、込み入ってない。
define(len,[ifelse($1,,0,[incr(len(substr($1,2)))])])
文字列を定義する"string"。
string(name,"STRING")
とすると
integer name(6)
data name(1)/LETS/
data name(2)/LETT/
data name(3)/LETR/
data name(4)/LETN/
data name(5)/LETG/
data name(6)/EOS/
と展開される。"string"の定義は、以下の通り。
define(string,[integer $1(len(substr($2,2)))
str($1,substr($2,2),0)
data $1(len(substr($2,2)))/EOS/
])
下請けルーチンの"str"は、以下の通り。
define(str,[ifelse($2,",, data $1(incr($3))/[LET]substr($2,1,1)/
[str($1,substr($2,2),incr($3))])])
macro.r4中の
string(balp,"()")
は、うまく展開できない。
引数付きマクロ処理 -- 機能拡張(1) ― 2017年04月14日 12:39
マクロが動くようになったので、バッチファイル"fim.bat"を作成し、マクロの展開に 使用する。
@echo off
rem fim.bat
cd ..\src
..\exe\include < %1.f | ..\exe\macro > %1.for
cd ..\bat
ここで、いくつかの有用な組み込み関数を追加する。追加する組み込み関数は、以下の通り。
- ifelse(a,b,c,d) -- aとbが文字列として等しければ、cをそうでなければ、dを返す。
- incr(a) -- aに+1した値を返す。
- substr(a,b,c) -- 文字列aのb文字目から、c文字を返す。cがなければ、文字列の最後まで返す。
- undef(a) -- マクロaを削除する。
- ifdef(a,b,c) -- マクロaが定義されていれば、bをそうでなければ、cを返す。
簡単な例を以下に示す。
define(EOF,-1)
define(EOS,-2)
define(MAXCARD,80)
define(MAXLINE,[incr(MAXCARD)]) -- MAXLINEは81になる。
define(FOO,0)
define(STR1,ABCDE)
define(STR2,12345)
ifdef([BAR],STR1,STR2) -- BARは定義されていないので、"12345"が返る。
ifdef([FOO],STR1,STR2) -- FOOは定義されているので、"ABCDE"が返る。
substr(STR1,3,2) -- "CD"が返る。
substr(STR2,3) -- "345"が返る。
undef([FOO]) -- FOOを削除する。"[]"が必要である。
define(compare,[ifelse($1,$2,YES,NO)]) -- 2つの引数が等しければ、YESを そうでなければNOを返すマクロ"compare"を定義する。
追加する組み込み関数のそれぞれの処理は、eval()の中で各処理ルーチンを呼び出す。新しいeval()は、以下の通り。
RATFOR版
# eval.r4 - expand args i through j: evaluate builtin or push back defn
subroutine eval(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer iindex,length
integer argno,k,m,n,t,td
include cmacro.ri
string digits "0123456789"
t = argstk(i)
td = evalst(t)
if (td == DEFTYPE)
call dodef(argstk,i,j)
else if (td == INCTYPE)
call doinc(argstk,i,j)
else if (td == SUBTYPE)
call dosub(argstk,i,j)
else if (td == IFTYPE)
call doif(argstk,i,j)
else if (td == UDFTYPE)
call doudf(argstk,i,j)
else if (td == IFDTYPE) {
call doifd(argstk,i,j)
}
else {
for (k = t + length(evalst(t)) - 1; k > t); k = k - 1)
if (evalst(k-1) != ARGFLAG)
call putbak(evalst(k))
else {
argno = iindex(digits,evalst(k)) - 1
if (argno >= 0) {
n = i + argno + 1
m = argstk(n)
call pbstr(evalst(m))
}
k = k - 1 # skip over $
}
if (k == t) # do last character
call putbak(evalst(k))
}
return
end
WATCOM Fortran 77版は以下の通り。
c eval.f - expand args i through j: evaluate builtin or push back defn
include ratfor.def
subroutine eval(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer iindex,length
integer argno,k,m,n,t,td,junk
include cmacro.fi
integer*1 digits(11)
data digits(1)/LET0/
data digits(2)/LET1/
data digits(3)/LET2/
data digits(4)/LET3/
data digits(5)/LET4/
data digits(6)/LET5/
data digits(7)/LET6/
data digits(8)/LET7/
data digits(9)/LET8/
data digits(10)/LET9/
data digits(11)/EOS/
t = argstk(i)
td = evalst(t)
if (td .eq. DEFTYPE) then
call dodef(argstk,i,j)
else if (td .eq. INCTYPE) then
call doinc(argstk,i,j)
else if (td .eq. SUBTYPE) then
call dosub(argstk,i,j)
else if (td .eq. IFTYPE) then
call doif(argstk,i,j)
else if (td .eq. UDFTYPE) then
call doudf(argstk,i,j)
else if (td .eq. IFDTYPE) then
call doifd(argstk,i,j)
else
k = t + length(evalst(t)) - 1
while (k .gt. t) do
if (evalst(k-1) .ne. ARGFLAG) then
call putbak(evalst(k))
else
argno = iindex(digits,evalst(k)) - 1
if (argno .ge. 0) then
n = i + argno + 1
m = argstk(n)
call pbstr(evalst(m))
end if
k = k - 1 ! skip over $
end if
k = k - 1
end while
if (k .eq. t) then
call putbak(evalst(k))
end if
end if
return
end
メインルーチンmacroでは、追加した組み込み関数を登録する必要がある。
RATFOR版は以下の通り。
# macro.r4 -- expand macros with arguments
program macro
character gettok
character defn(MAXDEF),t,token(MAXTOK)
integer lookup,push
integer ap,argstk(ARGSIZE),callst(CALLSIZE),nlb,plev(CALLSIZE)
string balp "()"
string defnam "define"
character deftyp(2)
data deftyp(1)/DEFTYPE/,deftyp(2)/EOS/
string incnam "incr"
character inctype(2)
data inctyp(1)/INCTYPE/,inctyp(2)/EOS/
string subnam "substr"
character subtype(2)
data subtyp(1)/SUBTYPE/,subtyp(2)/EOS/
string ifnam "ifelse"
character iftype(2)
data iftyp(1)/IFTYPE/,iftyp(2)/EOS/
string udfnam "ifelse"
character udftype(2)
data udftyp(1)/UDFTYPE/,udftyp(2)/EOS/
string ifdnam "ifdef"
character ifdtype(2)
data ifdtyp(1)/IFDTYPE/,ifdtyp(2)/EOS/
include cmacro.fi
call initfile
call inittbl
call initbuf
call instal(defnam,deftyp)
call instal(incnam,inctyp)
call instal(subnam,subtyp)
call instal(ifnam,iftyp)
call instal(udfnam,udftyp)
call instal(ifdnam,ifdtyp)
cp = 0
ap = 1
ep = 1
for (t = gettok(token,MAXTOK); t != EOF; t = gettok(token,MAXTOK)) {
if (t == ALPHA) {
if (lookup(token,defn) == NO)
call puttok(token)
else { # defined; put it in eval stack
cp = cp + 1
if (cp > CALLSIZE) then
call error('call stack overflow.')
callst(cp) = ap
ap = push(ep,argstk,ap)
call puttok(defn) # stack definition
call putchr(EOS)
ap = push(ep,argstk,ap)
call puttok(token) # stack name
call putchr(EOS)
ap = push(ep,argstk,ap)
t = gettok(token,MAXTOK) # peek at next
call pbstr(token)
if (t != LPAREN) # add ( ) if not present
call pbstr(balp)
plev(cp) = 0
}
}
else if (t == LBRACK) { # strip one level of [ ]
nlb = 1
repeat {
t = gettok(token,MAXTOK)
if (t == LBRACK)
nlb = nlb + 1
else if (t == RBRACK) {
nlb = nlb - 1
if (nlb == 0)
break
}
else if (t == EOF)
call error('EOF in string.')
call puttok(token)
}
else if (cp == 0) # not in a macro at all
call puttok(token)
else if (t == LPAREN)
if (plev(cp) > 0)
call puttok(token)
plev(cp) = plev(cp) + 1
else if (t == RPAREN) {
plev(cp) = plev(cp) - 1
if (plev(cp) .gt. 0)
call puttok(token)
else { # end of argument list
call putchr(EOS)
call eval(argstk,callst(cp),ap-1)
ap = callst(cp) # pop eval stack
ep = argstk(ap)
cp = cp - 1
}
else if ((t == COMMA) $ (plev(cp) == 1)) {
call putchr(EOS)
ap = push(ep,argstk,ap)
}
else
call puttok(token)
}
if (cp != 0)
call error('unexpected EOF.')
stop
end
WATCOM Fortran 77版は以下の通り。
c macro.f -- expand macros with arguments
include ratfor.def
program macro
integer*1 gettok
integer*1 defn(MAXDEF),t,token(MAXTOK)
integer lookup,push
integer ap,argstk(ARGSIZE),callst(CALLSIZE),nlb,plev(CALLSIZE)
integer*1 balp(3)
data balp(1)/LPAREN/
data balp(2)/RPAREN/
data balp(3)/EOS/
integer*1 defnam(7)
data defnam(1)/LETd/
data defnam(2)/LETe/
data defnam(3)/LETf/
data defnam(4)/LETi/
data defnam(5)/LETn/
data defnam(6)/LETe/
data defnam(7)/EOS/
integer*1 deftyp(2)
data deftyp(1)/DEFTYPE/
data deftyp(2)/EOS/
integer*1 incnam(5)
data incnam(1)/LETi/
data incnam(2)/LETn/
data incnam(3)/LETc/
data incnam(4)/LETr/
data incnam(5)/EOS/
integer*1 inctyp(2)
data inctyp(1)/INCTYPE/
data inctyp(2)/EOS/
integer*1 subnam(7)
data subnam(1)/LETs/
data subnam(2)/LETu/
data subnam(3)/LETb/
data subnam(4)/LETs/
data subnam(5)/LETt/
data subnam(6)/LETr/
data subnam(7)/EOS/
integer*1 subtyp(2)
data subtyp(1)/SUBTYPE/
data subtyp(2)/EOS/
integer*1 ifnam(7)
data ifnam(1)/LETi/
data ifnam(2)/LETf/
data ifnam(3)/LETe/
data ifnam(4)/LETl/
data ifnam(5)/LETs/
data ifnam(6)/LETe/
data ifnam(7)/EOS/
integer*1 iftyp(2)
data iftyp(1)/IFTYPE/
data iftyp(2)/EOS/
integer*1 udfnam(6)
data udfnam(1)/LETu/
data udfnam(2)/LETn/
data udfnam(3)/LETd/
data udfnam(4)/LETe/
data udfnam(5)/LETf/
data udfnam(6)/EOS/
integer*1 udftyp(2)
data udftyp(1)/UDFTYPE/
data udftyp(2)/EOS/
integer*1 ifdnam(6)
data ifdnam(1)/LETi/
data ifdnam(2)/LETf/
data ifdnam(3)/LETd/
data ifdnam(4)/LETe/
data ifdnam(5)/LETf/
data ifdnam(6)/EOS/
integer*1 ifdtyp(2)
data ifdtyp(1)/IFDTYPE/
data ifdtyp(2)/EOS/
include cmacro.fi
call initfile
call inittbl
call initbuf
call instal(defnam,deftyp)
call instal(incnam,inctyp)
call instal(subnam,subtyp)
call instal(ifnam,iftyp)
call instal(udfnam,udftyp)
call instal(ifdnam,ifdtyp)
cp = 0 ! current call stack pointer
ap = 1 ! next free position in argstk
ep = 1 ! next free position in evalst
t = gettok(token,MAXTOK)
while (t .ne. EOF) do
if (t .eq. ALPHA) then
if (lookup(token,defn) .eq. NO) then
call puttok(token)
else ! defined; put it in eval stack
cp = cp + 1
if (cp .gt. CALLSIZE) then
call error('call stack overflow.')
end if
callst(cp) = ap
ap = push(ep,argstk,ap)
call puttok(defn) ! stack definition
call putchr(EOS)
ap = push(ep,argstk,ap)
call puttok(token) ! stack name
call putchr(EOS)
ap = push(ep,argstk,ap)
t = gettok(token,MAXTOK) ! peek at next
call pbstr(token)
if (t .ne. LPAREN) then ! add ( ) if not present
call pbstr(balp)
end if
plev(cp) = 0
end if
else if (t .eq. LBRACK) then ! strip one level of [ ]
nlb = 1
loop
t = gettok(token,MAXTOK)
if (t .eq. LBRACK) then
nlb = nlb + 1
else if (t .eq. RBRACK) then
nlb = nlb - 1
if (nlb .eq. 0) then
exit
end if
else if (t .eq. EOF) then
call error('EOF in string.')
end if
call puttok(token)
end loop
else if (cp .eq. 0) then ! not in a macro at all
call puttok(token)
else if (t .eq. LPAREN) then
if (plev(cp) .gt. 0) then
call puttok(token)
end if
plev(cp) = plev(cp) + 1
else if (t .eq. RPAREN) then
plev(cp) = plev(cp) - 1
if (plev(cp) .gt. 0) then
call puttok(token)
else ! end of argument list
call putchr(EOS)
call eval(argstk,callst(cp),ap-1)
ap = callst(cp) ! pop eval stack
ep = argstk(ap)
cp = cp - 1
end if
else if ((t .eq. COMMA) .and. (plev(cp) .eq. 1)) then
call putchr(EOS)
ap = push(ep,argstk,ap)
else
call puttok(token)
end if
t = gettok(token,MAXTOK)
end while
if (cp .ne. 0) then
call error('unexpected EOF.')
end if
stop
end
引数付きマクロ処理(一部修正) ― 2017年01月26日 21:27
マクロの定義に、引数が使えるようになると、利便性が非常に向上する。簡単な例を 示す。まずは、マクロの定義は、以下のようになる。マクロgetc,putcの定義の中、$1が マクロの引数にである。引数は、$1から$9までである。
define(STDIN,5)
define(STDOUT,6)
define(getc,getch(STDIN,$1))
define(putc,putch(STDOUT,$1))
プログラム中では、以下のように、記述する。
c = getc(c)
call putc(c)
これが展開されると、以下のようになる。
c = getch(5,c)
call putch(6,c)
もう少し長いマクロの例を以下に示す。
define(BLANK,32)
define(TAB,9)
define(skipbl,while($1($2) == BLANK | $1($2) == TAB)
$2 = $2 + 1)
プログラム中では、
skipbl(s,i)
展開されると、
while(s(i) == 32 | s(i) == 9)
i = i + 1
読み込み中にマクロに出会ったら、引数も含めてマクロ評価用スタックに積む。 引数の中にマクロ呼び出しがあったら、新しいマクロ評価用スタック領域を取り、 スタックに積む。そして、マクロを完全に評価して、入力に送り返す。そして、元の マクロの評価を続ける。
マクロ評価用スタックevalstは配列で表現され、マクロの名前、定義型、 引数が入る。一方、配列argstkは、evalstに格納された文字列の場所の 位置を示す。いくつものモジュールで共通の用いられるevalstは以下の通り。
RATFOR版は、
# cmacro.ri
common /cmacro/cp,ep,evalst(EVALSIZE)
integer cp # current call stack pointer
integer ep # next free position in evalst
character evalst # evaluation stack
WATCOM fortran 77版は、
! cmacro.fi
common /cmacro/cp,ep,evalst(EVALSIZE)
integer cp ! current call stack pointer
integer ep ! next free position in evalst
integer*1 evalst ! evaluation stack
このマクロでは、マクロや組み込み操作は出現したとき、 その場で全て展開することになっているので、それではまずいことがある。 たとえば、defをdefineの同義語として定義したいとき、
define(def,define($1,$2))
とすれば良さそうだが、うまくいかない。まず、マクロ名"def"が、評価用スタックに積まれる。 次に、置き換え文字列"define($1,$2)"が評価されてしまい、"def"に対応する置き換え文字列が 空となってしまう。 これでは、目的を達成できないので、"["と"]"でくくられた範囲は、評価を遅らせる仕組みを 付け加える。
define(def,[define($1,$2)])
def(ABC,DEF)
とすると
ABC
は、変換されて、
DEF
となる。実は、引数なしのマクロプログラムのソースは、defineを通せない。 プログラム中のマクロ定義ではない"define"文字列がマクロの定義と 見間違えられてしまうのである。
引数なしのマクロには、"()"がつかない、これを特別扱いしないように、 "()"がついていないマクロに出会ったら、"()"を入力に送り返し、あたかも"()"が つぃているかのように振る舞わせる。
以上を踏まえた、引数付きマクロのRATFOR版は、以下の通り。
# macro.r4 -- expand macros with arguments
program macro
character gettok
integer*1 defn(MAXDEF),t,token(MAXTOK)
integer lookup,push
integer ap,argstk(ARGSIZE),callst(CALLSIZE),nlb,plev(CALLSIZE)
string balp "()"
string defnam "define"
character deftyp(2)
data deftyp(1)/DEFTYPE/,deftyp(2)/EOS/
include cmacro.fi
call initfile
call inittbl
call instal(defnam,deftyp)
cp = 0
ap = 1
ep = 1
for (t = gettok(token,MAXTOK); t != EOF; t = gettok(token,MAXTOK)) {
if (t == ALPHA) {
if (lookup(token,defn) == NO)
call puttok(token)
else { # defined; put it in eval stack
cp = cp + 1
if (cp > CALLSIZE) then
call error('call stack overflow.')
callst(cp) = ap
ap = push(ep,argstk,ap)
call puttok(defn) # stack definition
call putchr(EOS)
ap = push(ep,argstk,ap)
call puttok(token) # stack name
call putchr(EOS)
ap = push(ep,argstk,ap)
t = gettok(token,MAXTOK) # peek at next
call pbstr(token)
if (t != LPAREN) # add ( ) if not present
call pbstr(balp)
plev(cp) = 0
}
}
else if (t == LBRACK) { # strip one level of [ ]
nlb = 1
repeat {
t = gettok(token,MAXTOK)
if (t == LBRACK)
nlb = nlb + 1
else if (t == RBRACK) {
nlb = nlb - 1
if (nlb == 0)
break
}
else if (t == EOF)
call error('EOF in string.')
call puttok(token)
}
else if (cp == 0) # not in a macro at all
call puttok(token)
else if (t == LPAREN)
if (plev(cp) > 0)
call puttok(token)
plev(cp) = plev(cp) + 1
else if (t == RPAREN) {
plev(cp) = plev(cp) - 1
if (plev(cp) .gt. 0) then
call puttok(token)
else { # end of argument list
call putchr(EOS)
call eval(argstk,callst(cp),ap-1)
ap = callst(cp) # pop eval stack
ep = argstk(ap)
cp = cp - 1
}
else if ((t == COMMA) $ (plev(cp) == 1)) {
call putchr(EOS)
ap = push(ep,argstk,ap)
}
else
call puttok(token)
}
if (cp != 0)
call error('unexpected EOF.')
stop
end
WATCOM fortran 77版は、
! macro.f -- expand macros with arguments
include ratfor.def
program macro
integer*1 gettok
integer*1 defn(MAXDEF),t,token(MAXTOK)
integer lookup,push
integer ap,argstk(ARGSIZE),callst(CALLSIZE),nlb,plev(CALLSIZE)
integer*1 balp(3)
data balp(1)/LPAREN/
data balp(2)/RPAREN/
data balp(3)/EOS/
integer*1 defnam(7)
data defnam(1)/LETd/
data defnam(2)/LETe/
data defnam(3)/LETf/
data defnam(4)/LETi/
data defnam(5)/LETn/
data defnam(6)/LETe/
data defnam(7)/EOS/
integer*1 deftyp(2)
data deftyp(1)/DEFTYPE/
data deftyp(2)/EOS/
include cmacro.fi
call initfile
call inittbl
call instal(defnam,deftyp)
cp = 0 ! current call stack pointer
ap = 1 ! next free position in argstk
ep = 1 ! next free position in evalst
t = gettok(token,MAXTOK)
while (t .ne. EOF) do
if (t .eq. ALPHA) then
if (lookup(token,defn) .eq. NO) then
call puttok(token)
else ! defined; put it in eval stack
cp = cp + 1
if (cp .gt. CALLSIZE) then
call error('call stack overflow.')
end if
callst(cp) = ap
ap = push(ep,argstk,ap)
call puttok(defn) ! stack definition
call putchr(EOS)
ap = push(ep,argstk,ap)
call puttok(token) ! stack name
call putchr(EOS)
ap = push(ep,argstk,ap)
t = gettok(token,MAXTOK) ! peek at next
call pbstr(token)
if (t .ne. LPAREN) then ! add ( ) if not present
call pbstr(balp)
end if
plev(cp) = 0
end if
else if (t .eq. LBRACK) then ! strip one level of [ ]
nlb = 1
loop
t = gettok(token,MAXTOK)
if (t .eq. LBRACK) then
nlb = nlb + 1
else if (t .eq. RBRACK) then
nlb = nlb - 1
if (nlb .eq. 0) then
exit
end if
else if (t .eq. EOF) then
call error('EOF in string.')
end if
call puttok(token)
end loop
else if (cp .eq. 0) then ! not in a macro at all
call puttok(token)
else if (t .eq. LPAREN) then
if (plev(cp) .gt. 0) then
call puttok(token)
end if
plev(cp) = plev(cp) + 1
else if (t .eq. RPAREN) then
plev(cp) = plev(cp) - 1
if (plev(cp) .gt. 0) then
call puttok(token)
else ! end of argument list
call putchr(EOS)
call eval(argstk,callst(cp),ap-1)
ap = callst(cp) ! pop eval stack
ep = argstk(ap)
cp = cp - 1
end if
else if ((t .eq. COMMA) .and. (plev(cp) .eq. 1)) then
call putchr(EOS)
ap = push(ep,argstk,ap)
else
call puttok(token)
end if
t = gettok(token,MAXTOK)
end while
if (cp .ne. 0) then
call error('unexpected EOF.')
end if
stop
end
下請けルーチンputtok()のRATFOR版は、以下の通り。
# puttok.r4 -- put a token either on output or into evaluation stack
subroutine puttok(str)
character str(MAXTOK)
integer i
for (i = 1; str(i) != EOS; i = i + 1)
call putchr(str(i))
return
end
WATCOM fortran 77版は、
! puttok.f -- put a token either on output or into evaluation stack
include ratfor.def
subroutine puttok(str)
integer*1 str(MAXTOK)
integer i
i = 1
while (str(i) .ne. EOS) do
call putchr(str(i))
i = i + 1
end while
return
end
下請けルーチンputchr()のRATFOR版は、以下の通り。
# putchr -- put single char on output or into eveluation stack
subroutine putchr(c)
character c
include cmacror.ri
if (cp == 0)
call putc(c)
else {
if (ep > EVALSIZE)
call error('eveluation stack overflow.')
evalst(ep) = c
ep = ep + 1
}
return
end
WATCOM fortran 77版は、
! putchr -- put single char on output or into eveluation stack
include ratfor.def
subroutine putchr(c)
integer*1 c
include cmacro.fi
if (cp .eq. 0) then
call putc(c)
else
if (ep .gt. EVALSIZE) then
call error('eveluation stack overflow.')
end if
evalst(ep) = c
ep = ep + 1
end if
return
end
下請けルーチンeval()のRATFOR版は、以下の通り。
# eval.r4 - expand args i through j: evaluate builtin or push back defn
subroutine eval(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer iindex,length
integer argno,k,m,n,t,td
include cmacro.ri
string digits "0123456789"
t = argstk(i)
td = evalst(t)
if (td == DEFTYPE)
call dodef(argstk,i,j)
else {
for (k = t + length(evalst(t)) - 1; k > t); k = k - 1)
if (evalst(k-1) != ARGFLAG)
call putbak(evalst(k))
else {
argno = iindex(digits,evalst(k)) - 1
if (argno >= 0) {
n = i + argno + 1
m = argstk(n)
call pbstr(evalst(m))
}
k = k - 1 # skip over $
}
if (k == t) # do last character
call putbak(evalst(k))
}
return
end
WATCOM fortran 77版は、
! eval.f - expand args i through j: evaluate builtin or push back defn
include ratfor.def
subroutine eval(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer iindex,length
integer argno,k,m,n,t,td
include cmacro.fi
integer*1 digits(11)
data digits(1)/LET0/
data digits(2)/LET1/
data digits(3)/LET2/
data digits(4)/LET3/
data digits(5)/LET4/
data digits(6)/LET5/
data digits(7)/LET6/
data digits(8)/LET7/
data digits(9)/LET8/
data digits(10)/LET9/
data digits(11)/EOS/
t = argstk(i)
td = evalst(t)
if (td .eq. DEFTYPE) then
call dodef(argstk,i,j)
else
k = t + length(evalst(t)) - 1
while (k .gt. t) do
if (evalst(k-1) .ne. ARGFLAG) then
call putbak(evalst(k))
else
argno = iindex(digits,evalst(k)) - 1
if (argno .ge. 0) then
n = i + argno + 1
m = argstk(n)
call pbstr(evalst(m))
end if
k = k - 1 ! skip over $
end if
k = k - 1
end while
if (k .eq. t) then
call putbak(evalst(k))
end if
end if
return
end
下請けルーチンdodef()のRATFOR版は、以下の通り。
# dodef.rf -- install definition in table
subroutine dodef(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer a2,a3
include cmacro.ri
if (j-i .gt. 2) {
a2 = argstk(i+2)
a3 = argstk(i+3)
call instal(evalst(a2),evalst(a3)) # subarrays
}
return
end
WATCOM fortran 77版は、
! dodef.f -- install definition in table
include ratfor.def
subroutine dodef(argstk,i,j)
integer i,j,argstk(ARGSIZE)
integer a2,a3
include cmacro.fi
if (j-i .gt. 2) then
a2 = argstk(i+2)
a3 = argstk(i+3)
call instal(evalst(a2),evalst(a3)) ! subarrays
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
最近のコメント