目次
前
次
VTLコンバータ(AWK)
Personal Computerの黎明期には、使えるメモリが
少ないことから、BASIC言語プログラムをVTL(Very Tiny
Language)で記述して、テキストサイズを小さくして
いました。
アセンブリ言語ではなく、高レベル言語を使い
より短い時間でプログラムを開発したいという
要求があったので、GAME言語が生まれました。
VTLの一種のMicroBasicは、次のように記述します。
1000 "1"
1010 A=0
1020 #=2000 A=A+1
1030 ¥ END
2000 "2"
2010 !=3000
2020 #=1030
3000 " sub routine " ¥
3010 B=0
3020 ;=B>10 #=3040 ?#=B
3030 B=B+1 #=3020
3040 ]
記号が含まれて、わかりにくいですが、コンピュータが
理解できれば問題ありません。
もとのBASICコードは、以下でした。
1000 "1"
1010 A=0
1020 GOTO 2000 A=A+1
1030 ¥ END
2000 "2"
2010 GOSUB 3000
2020 GOTO 1030
3000 " sub routine " ¥
3010 B=0
3020 if B>10 goto 3040 PRINT# B
3030 B=B+1 goto 3020
3040 RETURN
今回は、人間が理解しやすい予約語(GOTO、GOSUB等)を
VTLの記号を利用した記述に変換します。
変換する予約語と対応処理を考えます。
IF
MicroBasicでは、IFを";="で表現します。
"IF"を発見したら、";="に置換して
スペースを入れないで、条件式を続けます。
言葉では面倒に思えます、スクリプトは単純です。
if ( tmp == "IF" ) {
tmp = ";=" $j
flag = 1
k = j
}
文字列を記号に置換え、さらに条件式を続けます。
また、フラグをセットし、条件式の位置を記憶します。
条件式の位置を記憶しておき、条件式のフィールドを
表示するとき、文字列に含まれている条件式を二重で
出力しないようにスキップします。
GOTO
MicroBasicでは、GOTOを"#="で表現します。
"GOTO"を発見したら、"#="に置換して
スペースを入れないで、行番号を続けます。
処理は、次のように書けます。
if ( tmp == "GOTO" ) {
tmp = "#=" $j
flag = 1
k = j
}
GOSUB
MicroBasicでは、GOSUBを"!="で表現します。
"GOSUB"を発見したら、"!="に置換して
スペースを入れないで、行番号を続けます。
処理は、以下。
if ( tmp == "GOSUB" ) {
tmp = "!=" $j
flag = 1
k = j
}
RETURN
MicroBasicでは、RETURNを"]"で表現します。
"RETURN"を発見したら、"]"に置換します。
RETURNの場合、もどる行番号は確定しているので
単純な置換えだけになります。
if ( tmp == "RETURN" ) {
tmp = "]"
flag = 1
}
PRINT
MicroBasicでは、PRINTを"?"で表現します。
"PRINT"を発見したら、"?"に置換します。
PRINTの場合、書式変換の記号がついているので
PRINTに続いて指定されている変換記号を取出し
"?"の右に置きます。
言葉では面倒に思えます、スクリプトは単純です。
if ( substr(tmp,1,5) == "PRINT" ) {
tmp = "?" substr(tmp,6,1) "=" $j
flag = 1
k = j
}
ここまでで、変換処理の内容が固まったので
前処理と後処理を考えていきます。
前処理
プログラムを構成する文字列が、大文字、小文字の
混在では、変換処理を記述しにくいので、大文字に
変換します。
tmp = toupper( $i )
現在のフィールドが、変換が必要な文字列になって
いないとフラグをクリアしておきます。
flag = 0
変換対象文字列の場合、次のフィールドに行番号や条件式
が含まれるので、現フィールドの次のフィールド位置を
記憶しておきます。
j = i+1
後処理
表示する文字列が、変換対象となる文字列かどうかで
場合分けして、最終表示文字列を決定します。
変換対象であるかどうかをフラグで指定するので
フラグ値を利用し、最終表示文字列を決定します。
stmp = $i
if ( flag == 1 ) {
stmp = tmp
}
変換対象文字列には行番号や条件式が付加されるので
行番号や条件式の表示をスキップします。
if ( i != k ) {
printf("%s ",stmp)
}
1行のスキャンが終了して、全文字列を表示したなら
改行します。
if ( i == NF ) {
printf("\n");
}
例外処理
AWKは"END"が予約語になっているので、この文字列表示は
陽に指定します。
if ( $i == "END" ) {
printf("END");
}
最終スクリプトは、以下となりました。
{
for ( i = 1 ; i < NF+1 ; i++ ) {
# flag
flag = 0
# next field
j = i+1
# convert
tmp = toupper( $i )
# exception reserved word END
if ( $i == "END" ) {
printf("END")
}
# IF
if ( tmp == "IF" ) {
tmp = ";=" $j
flag = 1
k = j
}
# GOTO
if ( tmp == "GOTO" ) {
tmp = "#=" $j
flag = 1
k = j
}
# GOSUB
if ( tmp == "GOSUB" ) {
tmp = "!=" $j
flag = 1
k = j
}
# RETURN
if ( tmp == "RETURN" ) {
tmp = "]"
flag = 1
}
# PRINT
if ( substr(tmp,1,5) == "PRINT" ) {
tmp = "?" substr(tmp,6,1) "=" $j
flag = 1
k = j
}
# generate VTL statement
stmp = $i
if ( flag == 1 ) {
stmp = tmp
}
# show
if ( i != k ) {
printf("%s ",stmp)
}
# new line
if ( i == NF ) {
printf("\n")
}
}
}
使い方は、簡単です。
CUIで次のように操作します。
変換した内容が欲しいなら、I/Oリダイレクトを
利用します。
GAME言語のように、仕様が似ている場合、記号で表現する
内容を自由に置換えることが可能でしょう。
AWKでは正規表現で文字列の照合ができますが、Perlやsedで
使われる内容と若干差異があるので、等号による照合で処理
しました。
文字列の結合は、文字列を代入するための「=」の右に
文字列を並べる書式としました。
関数や手続きを定義するよりも、より手軽に扱える方法で
変換を実現しています。
目次
前
次