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
最近のコメント