SHOEISHA iD

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

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

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

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

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

変数の宣言、代入、表示と四則演算

nullの取り扱い

 ところで、こんなことをやってみます。

test4.txt
Dim tester As Long
tester = 45
print "$tester|が入っているんです。"

 これをちょっとアセンブルしてみます。

エラー発生
C:\Documents and Settings\????????\My Documents\samples\codezine\vefbl\build\tes
t4.txt.asm(13) : error A2047: empty (null) string

 エラーが発生してしまいました。エラーメッセージを見てみると、empty (null) string、つまり変数に空っぽの文字列を代入しようとしたことが原因のようです。アセンブリを見てみると、以下のようになっています。

STR0 db "", 0

 このような行をどうやってなくせばいいのでしょうか。ちょっとプログラムを見直してみます。

build.nako(一部)
違えば、もし、対象が「$」&&文字列中がはいならば、
               //$が変数名の最初(Perlを参考にしました)
 文字列中は、いいえ。
 もし、最新命令が「print」&&バッファ<>空ならば、
  アセンブリデータに「STR{文字列数} db "{バッファ}", 0」を配列追加。
  アセンブリに「invoke OutputString, offset STR{文字列数}」を配列追加。
  文字列数は文字列数+1。
  バッファは空。

 ここで<>が登場しました。これは、C言語での!=と同じ意味です。この状態でコンパイルすると、アセンブルに成功し、作った実行ファイルも予想通りの動作をしてくれるようになります。バッファが空かどうか確かめているのは、アセンブリデータの行で代入する文字列の内容が、変数バッファの中身と同じだからです。

 ところで、変数を文字列の最初に書いたらエラーが起きたという事は、最後に書いてもエラーが起きるということなのでしょうか。試しにやってみます。

test4.txt
Dim tester As Long
tester = 45
print "入っていたのは、な、なんと、$tester|"

 案の定、同じようなエラーが起きてしまいました。どこを直せばいいのでしょうか。違えば、もし、対象が「|」&&文字列中がいいえならば、では、バッファを空にしているのですが、文字列は除外してますし最新命令の内容も変えていません。しかもこの後に改行があります。ということは、改行分岐の中のprintで表示する箇所ということになり、ここを変える必要がありそうです。

build.nako(改行分岐の一部)
違えば、もし、対象が改行ならば、
 バッファを表示。
 もし、予約名スィッチがオンならば、
  予約名スィッチはオフ。
  予約名は「{予約名}{改行}」。
 もし、最新命令が「print」&&バッファ<>空ならば、
  アセンブリデータに「STR{文字列数} db "{バッファ}", 0」を配列追加。
  アセンブリに「invoke OutputString, offset STR{文字列数}」を配列追加。
  文字列数は文字列数+1。
 違えば、もし、最新命令が「msgbox」ならば、
  アセンブリデータに「STR{文字列数} db "{バッファ¥0}", 0」を配列追加。
  もし、バッファ¥1が空ならば、
   アセンブリに「invoke MessageBoxA, 0, offset STR{文字列数}, offset STR{文字列数}, 0」を配列追加。
  違えば、
   アセンブリデータに「STR{文字列数+1} db "{バッファ¥1}", 0」を配列追加。
   アセンブリに「invoke MessageBoxA, 0, offset STR{文字列数}, offset STR{文字列数+1}, 0」を配列追加。
   文字列数は文字列数+1。    //文字列を1つ多く使ったので
  文字列数は文字列数+1。
 違えば、もし、最新命令が「Dim」ならば、

 これでさっきのスクリプトをコンパイルすると、正常にアセンブルすることができます。

 さて、クイズです。このままのコンパイラで、以下のプログラムを正常にアセンブルできるでしょうか?

test4.txt
Dim tester As Long
Dim sononi As Long

tester = 45
sononi = 73

print "testerには$tester|が、sononiには$sononi|が入っていました。ではtesterに入っているのは?:答え:$tester|"

 さっきまでは1つのprintで変数を1つだけ表示していましたが、今度は1つのprintで一度に2つ以上の変数を表示するというものです。

 答えは、「正常にアセンブルできる」です。その理由がここに書いてあります(注:どこも変更していません)。

build.nako(一部)
違えば、もし、対象が「$」&&文字列中がはいならば、
               //変数名の最初に$をつける(Perlを参考にしました)
 文字列中は、いいえ。
 もし、最新命令が「print」&&バッファ<>空ならば、
  アセンブリデータに「STR{文字列数} db "{バッファ}", 0」を配列追加。
  アセンブリに「invoke OutputString, offset STR{文字列数}」を配列追加。
  文字列数は文字列数+1。
  バッファは空。

 太字のところで同時にprint関数の処理をしてしまっているので、正常にアセンブルすることができるのです。

演算の実装

足し算

 ここからは四則演算ができるようにしていきます。第1回でのソースコードから足し算の部分を抜き出してみました。

MASMで足し算
    mov eax, 13
    push eax
    mov edx, 35
    push edx
    pop edx
    pop eax
    add eax, edx
    push eax
    pop eax
    mov kekka, eax
    invoke OutputInteger, kekka

 足し算をし、最後の行で結果kekkaを出力しています。式について詳しく書くと以下のようになります。

MASMで足し算を研究
    mov eax, (足される数)
    push eax
    mov edx, (足す数)
    push edx
    pop edx
    pop eax
    add eax, edx
    push eax
    pop eax
    mov (代入する変数), eax
    invoke OutputInteger, (代入する変数)

 それでは置き換えてみます。以下のプログラムをコンパイルできるようにしましょう。

test5.txt
Dim tas1 As Long
Dim tas2 As Long
Dim kekka As Long

tas1 = 4
tas2 = 7

kekka = tas1 + tas2
print "$tas1| + $tas2| = $kekka|"

 結果はたいたい予想できると思いますが、次のように出力されるはずです。

4 + 7 = 11

 これを実現してみます。

build.nako(一部)
(省略)

バッファは空。             //変数「空」の中身には何も入っていません
予約名は空。               //予約名
予約名スィッチは、オフ。   //現在の位置が予約名の中であるか
文字列中は、いいえ。       //現在の位置が文字列の中であるか
最新命令は、空。           //さっき指定されたばかりの命令
アセンブリは空。           //出力するアセンブリ
アセンブリデータは空。     //出力するアセンブリデータ
文字列数は0。              //文字列の数
変更フラグは0。            //変数へ値を代入中?
足し算フラグは0。        //足し算中

(省略)

違えば、もし、対象が「=」&&文字列中がいいえならば、
 バッファは「{バッファ}{改行}」。
 変更フラグは、はい。
違えば、もし、対象が「+」&&文字列中がいいえならば、
 バッファは「{バッファ}{改行}」。
 足し算フラグは、はい。
 変更フラグは、いいえ。
違えば、もし、対象が「,」&&文字列中がいいえならば、
 バッファは「{バッファ}{改行}」。

(省略)

違えば、もし、対象が改行ならば、

(省略)

 違えば、もし、変更フラグがはいならば、
  変更フラグは、いいえ。
  バッファのタブを空に置き換えて、バッファに代入。
  バッファ¥0の「 」を空に置き換えて、変数名前に代入。
  バッファ¥1の「 」を空に置き換えて、変数名後に代入。
  アセンブリに「mov {変数名前},{変数名後}」を配列追加。
 違えば、もし、足し算フラグがはいならば、
  足し算フラグは、いいえ。
  バッファの「    」を空に置き換えて、バッファに代入。
  バッファ¥0の「 」を空に置き換えて、代入変数名に代入。
  バッファ¥1の「 」を空に置き換えて、変数名前に代入。
  バッファ¥2の「 」を空に置き換えて、変数名後に代入。
  アセンブリに「mov eax, {変数名前}
push eax
mov edx, {変数名後}
push edx
pop edx
pop eax
add eax, edx
push eax
pop eax
mov {代入変数名}, eax」を配列追加。
 バッファは空。

(省略)

 足し算フラグはいにして変更フラグいいえにしているのは、改行分岐の時、変更フラグはいのままだと次のように出力されエラーになってしまうからです。

mov kekka,tas1 + tas2

 変更フラグはいの分岐を先にしているので、このままだとバッファ¥1全体を代入する内容と置き換えてしまいます。そのためいいえにしているのです。これをコンパイルすると予想通りの文字が表示されます。

引き算、掛け算、割り算の一括実装

 さて、残りの引き算、掛け算、割り算もできるように実装します。正常に動作すれば予想結果のようになるはずです。

test5.txt
Dim tas1 As Long
Dim tas2 As Long
Dim kekka As Long

tas1 = 8
tas2 = 4

kekka = tas1 + tas2
print "$tas1| + $tas2| = $kekka|"

kekka = tas1 - tas2
print " : $tas1| - $tas2| = $kekka|"

kekka = tas1 * tas2
print " : $tas1| * $tas2| = $kekka|"

kekka = tas1 / tas2
print " : $tas1| / $tas2| = $kekka|"

予想結果
8 + 4 = 12 : 8 - 4 = 4 : 8 * 4 = 32 : 8 / 4 = 2

 実装した「build.nako」はサンプルソースを参考にしてください。

次のページ
インタプリタ言語のバグの怖さ

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

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

もっと読む

この記事の著者

KMY(かみぃ)

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1339 2008/08/26 14:03

イベント

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

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

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

メールバックナンバー