SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

MASMとなでしこによるオリジナルコンパイラの制作

MASMとなでしこによるオリジナルコンパイラの制作 3

コンパイラを作成してコンパイルをする

コンパイラの作成

 アセンブルできたところで、今度はコンパイラを作成します。

まずはそのままコピー

 トークン分割の部分のコンパイラへの移植は、長いわけではないのですが、じっくり理解してほしいので細かく分けて解説します。前回作成した「tokentest.nako」該当部分を、「build.nako」にコピーします。

build.nako
もし、コマンドライン¥1が空ならば、
「ファイル名を指定してください」と表示。
終わる。
コマンドライン¥1を開く。
それを表示。

バッファは空。     //変数「空」の中身には何も入っていません
文字列中は、いいえ。  //現在の位置が文字列の中であるか
最新命令は、空。    //さっき指定されたばかりの命令

「msgbox "やってみました"
print "やってみました"」を文字列分解して、反復
        //このようにすれば文字列に変数を埋め込めます
 もし、バッファが「print」ならば、
  最新命令はバッファ。
  バッファは空。
        //まだ他の場所にprintがあっても
        //「print(なんとか)print」などとなってしまい
        //検出できなくなる
 違えば、もし、バッファが「msgbox」ならば、
  最新命令はバッファ。
  バッファは空。
 違えば、もし、対象が「"」ならば、
            //「"」は一般的な言語では文字列の最初を表す
  もし、文字列中がはいならば、
   文字列中は、いいえ。
   もし、最新命令が「print」ならば、
    バッファを表示。
    バッファは空。
   違えば、もし、最新命令が「msgbox」ならば、
    バッファを言う。
    バッファは空。
  違えば、
   文字列中は、はい。
   バッファは空。    //文字列の最初についてしまう「"」対策
 違えば、もし、対象が改行でなければ、
  バッファは「{バッファ}{対象}」。
        //上記以外の場合
        //printの後の半角スペースとかは
        //この行から5行上で対処してしまっています

アセンブリ用の変数

 この「build.nako」には、まだアセンブリを出力する部分がありません。出力するアセンブリファイル名を記述します。今回は、コマンドライン¥1に拡張子「.asm」を付与したものにします。

build.nako(太字が修正箇所)
もし、コマンドライン¥1が空ならば、
「ファイル名を指定してください」と表示。
終わる。
コマンドライン¥1を開く。
それを表示。

バッファは空。
文字列中は、いいえ。
最新命令は、空。

「msgbox "やってみました"
print "やってみました"」を文字列分解して、反復
       //このようにすれば文字列に変数を埋め込めます
 もし、バッファが「print」ならば、
  最新命令はバッファ。
  バッファは空。
 違えば、もし、バッファが「msgbox」ならば、
  最新命令はバッファ。
  バッファは空。
 違えば、もし、対象が「"」ならば、
  もし、文字列中がはいならば、
   文字列中は、いいえ。
   もし、最新命令が「print」ならば、
    バッファを表示。
    バッファは空。
   違えば、もし、最新命令が「msgbox」ならば、
    バッファを言う。
    バッファは空。
  違えば、
   文字列中は、はい。
   バッファは空。
 違えば、もし、対象が改行でなければ、
  バッファは「{バッファ}{対象}」。

「.586
.model flat, stdcall

include iolib.inc
includelib iolib.lib
include kernel32.inc
includelib kernel32.lib

.data
        PrintText db "Hello Word!", 0

.code
_start:
        invoke OutputString, offset PrintText
        invoke ExitProcess, 0
end _start
」を「{コマンドライン¥1}.asm」に保存。

 なお、テキストをファイルに保存するときは

SをFに保存

 と記述します。これでテキストSをファイルFに保存します。

 しかし出力するアセンブリは、例えば「script.txt」をコンパイルした時、「script.txt.asm」というファイル名で保存されることになります。これでは格好悪いので、一応、仮の拡張子として、「.txt」を使用する事にします。

アセンブリの出力

 出力するアセンブリの変数には、データを格納する.data専用のアセンブリデータ、プログラム本体を書く.code専用のアセンブリの2つを用意します。

build.nako(太字が修正箇所)
もし、コマンドライン¥1が空ならば、
「ファイル名を指定してください」と表示。
終わる。
コマンドライン¥1を開く。
それを表示。

バッファは空。
文字列中は、いいえ。
最新命令は、空。
アセンブリは空。        //出力するアセンブリ
アセンブリデータは空。     //出力するアセンブリデータ

「msgbox "やってみました"
print "やってみました"」を文字列分解して、反復
 もし、バッファが「print」ならば、
  最新命令はバッファ。
  バッファは空。
 違えば、もし、バッファが「msgbox」ならば、
  最新命令はバッファ。
  バッファは空。
 違えば、もし、対象が「"」ならば、
  もし、文字列中がはいならば、
   文字列中は、いいえ。
   もし、最新命令が「print」ならば、
    バッファを表示。
    バッファは空。
   違えば、もし、最新命令が「msgbox」ならば、
    バッファを言う。
    バッファは空。
  違えば、
   文字列中は、はい。
   バッファは空。
 違えば、もし、対象が改行でなければ、
  バッファは「{バッファ}{対象}」。

// 「.data」の下、「_start:」の下が変わっています
「.586
.model flat, stdcall

include iolib.inc
includelib iolib.lib
include kernel32.inc
includelib kernel32.lib

.data
{アセンブリデータ}

.code
_start:
{アセンブリ}
invoke ExitProcess, 0
end _start
」を「{コマンドライン¥1}.asm」に保存。

 .codeの部分で、ExitProcess関数の部分を残しています。これがないとWindows保護例外エラーが発生するからです。また、プログラミングする人がこのコードを出力する部分を忘れてしまうと、実行時にエラーが発生してしまいます。すべての場合において必ず出力するようにしておきましょう。

アセンブリコードへの配列追加

 変数にアセンブリを追加するのは、バッファを表示している箇所がよさそうです。また、アセンブリコードに配列を追加するには「配列追加」の命令文を使います。これは、配列型変数Hに文字列Sを新しい配列要素として追加します。例えば、

テスト配列は「あいう
えおか
きくけ
こさし」。
テスト配列に「すせそ」を配列追加。
テスト配列を表示。

 とすると、

あいう
えおか
きくけ
こさし
すせそ

 の、結果が得られます。文字数は違っていても構いません。要するに、変数の最後に改行と文字列を追加すると思ってください。実際にこれを使ったプログラムを見てみましょう。

build.nako(太字が修正箇所)
もし、コマンドライン¥1が空ならば、
「ファイル名を指定してください」と表示。
終わる。
コマンドライン¥1を開く。
それを表示。

バッファは空。
文字列中は、いいえ。
最新命令は、空。
アセンブリは空。
アセンブリデータは空。
文字列数は0。    //文字列の数

コマンドライン¥1を開く。
それを文字列分解して、反復
 もし、バッファが「print」ならば、
  最新命令はバッファ。
  バッファは空。
 違えば、もし、バッファが「msgbox」ならば、
  最新命令はバッファ。
  バッファは空。
 違えば、もし、対象が「"」ならば、
  もし、文字列中がはいならば、
   文字列中は、いいえ。
   もし、最新命令が「print」ならば、
    アセンブリデータに
    「STR{文字列数} db "{バッファ}", 0」を配列追加。
    アセンブリに
    「invoke OutputString, offset STR{文字列数}」を配列追加。
    文字列数は文字列数+1。
    バッファは空。
   違えば、もし、最新命令が「msgbox」ならば、
    アセンブリデータに
    「STR{文字列数} db "{バッファ}", 0」を配列追加。
    アセンブリに
    「invoke MessageBoxA, 0, offset STR{文字列数}, 
    offset STR{文字列数}, 0」を配列追加。文字列数は文字列数+1。
    バッファは空。
  違えば、
   文字列中は、はい。
   バッファは空。
 違えば、もし、対象が改行でなければ、
  バッファは「{バッファ}{対象}」。

「.586
.model flat, stdcall

include iolib.inc
includelib iolib.lib
include kernel32.inc
includelib kernel32.lib

.data
{アセンブリデータ}

.code
_start:
{アセンブリ}
invoke ExitProcess, 0
end _start
」を「{コマンドライン¥1}.asm」に保存。

 なんとなく分かるでしょうか。変数文字列数はなぜ必要かと言うと、1つのプログラム内で複数の文字列を使う時、同じ変数を使いまわすわけにもいきません。ここでは、カウントしながら変数名に連番をつけることで、多くの変数を用意しています。また、開いたファイルの内容をコンパイルするように修正しました。

次のページ
コンパイラ完成

この記事は参考になりましたか?

MASMとなでしこによるオリジナルコンパイラの制作連載記事一覧

もっと読む

この記事の著者

KMY(かみぃ)

とあるごく平凡な中学生。兵庫県在住。日本語プログラミング言語「なでしこ」に惹かれ、たくさんのプログラムを作り利用している。なでしこ以外にも、他の言語を心得ており、HTMLがなでしこの次に得意であり、Perlが第3位(かも)。ベクターにも多彩な作品を残している。作文がど苦手。KMYpage

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/766 2007/03/08 19:10

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー