目次

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で
 使われる内容と若干差異があるので、等号による照合で処理
 しました。

 文字列の結合は、文字列を代入するための「=」の右に
 文字列を並べる書式としました。

 関数や手続きを定義するよりも、より手軽に扱える方法で
 変換を実現しています。


目次

inserted by FC2 system