はじめに
本連載では、最近の日本語プログラミング言語の中で高いシェアを占める「なでしこ」を使用して、BASICライクな自作言語のコンパイラを作成します。
前回、「MASMとなでしこによるオリジナルコンパイラの制作 2」では、自作プログラミング言語「VEFBL」の言語仕様に沿った、簡単なインタプリタ環境を作りました。今回は、そのインタプリタ環境に用いたプログラムを応用して、いよいよコンパイラを作成します。
過去の記事
対象読者
技術レベルは特に問いませんが、なでしこの簡単な文法を前もって習得していることが望ましいです。「自分で作ったプログラミング言語でソフトを作りたい!」と考えている方も歓迎します。
また、プラットフォームはWindows XPを推奨します。MASMアセンブラのインストーラが、現在Windows XP以降のWindows NT系OSでしか動作確認できていないからです。これは、インストーラの起動時に使うWin32APIがWindows 9x系OSにないため、Windows保護例外エラーが発生するのが原因です。また、MASMはMac OSやLinuxなどでは動作しません。
作成の流れ
コンパイラを作成するにあたって、本稿では次の順で説明していきます。
- print関数の理解
- コードジェネレータの設定
- コンパイラの作成
- コンパイラの実行
print関数を理解する
「VEFBL」と表示してみる
前回の記事では次の内容の「tokentest.nako」を作成し、「Hello Word!」を表示させることができました。
バッファは空。 文字列中は、いいえ。 最新命令は、空。 「print "Hello Word!"」を文字列分解して、反復 もし、バッファが「print」ならば、 「printを検出しました」と言う。 最新命令はバッファ。 バッファは空。 違えば、もし、対象が「"」ならば、 もし、文字列中がはいならば、 文字列中は、いいえ。 もし、最新命令が「print」ならば、 バッファを表示。 バッファは空。 違えば、 文字列中は、はい。 バッファは空。 //文字列の最初についてしまう「"」対策 違えば //ここは対象が「"」じゃない時の処理です バッファは「{バッファ}{対象}」。 //上記以外の場合 //printの後の半角スペースとかは //この行から5行上で対処してしまっています
今回はまず、print関数の使い方を説明していきます。例えば表示させる文字列を「VEFBL」にしたいときは、どこを修正すればいいのでしょうか。「tokentest.nako」を見てみると(できれば、なでしこエディタ-nakopad.exeで開くことをおすすめします)、文字列を表示しているプログラムの部分は5行目であることが分かります。
「print "Hello Word!"」を文字列分解して、反復
この部分を変更すれば、他の文字列を表示することが出来そうです。試しにやってみます。
「print "VEFBL"」を文字列分解して、反復
VEFBL
きちんと他の文字列も表示できることができました。
さらに改行して、もう1つprint関数を行使してみるとどうなるでしょう?
「print "VEFBL" print "Hello Me!"」を文字列分解して、反復
VEFBL Hello Me!
予想通り、2つの文字列が改行されて表示されました。なお、「print検出しました」の行は今後必要ないので、9行目は消しておきます。
今度は、メッセージボックスを表示する関数msgbox関数を作ります。作り方は、print関数と似たプログラムになります。
バッファは空。 文字列中は、いいえ。 //現在の位置が文字列の中であるか 最新命令は、空。 //さっき指定されたばかりの命令 「msgbox "やってみました"」を文字列分解して、反復 // このようにすれば文字列に変数を埋め込めます もし、バッファが「print」ならば、 最新命令はバッファ。 バッファは空。 // まだ他の場所にprintがあっても // 「print(なんとか)print」などとなってしまい // 検出できなくなる 違えば、もし、バッファが「msgbox」ならば、 最新命令はバッファ。 バッファは空。 違えば、もし、対象が「"」ならば、 // 「"」は一般的な言語では文字列の最初を表す もし、文字列中がはいならば、 文字列中は、いいえ。 もし、最新命令が「print」ならば、 バッファを表示。 バッファは空。 違えば、もし、最新命令が「msgbox」ならば、 バッファを言う。 バッファは空。 違えば、 文字列中は、はい。 バッファは空。 // 文字列の最初についてしまう「"」対策 違えば、 バッファは「{バッファ}{対象}」。
作成したら実行してみましょう。さらに、5行目に1行追加してみます。
「msgbox "やってみました" print "やってみました"」を文字列分解して、反復
これを実行してみると、「やってみました」のメッセージボックスが2つも出てきました。2行目のprint関数は働いていないらしく、何も表示されていません。
デバッグ
原因を探るためにデバッグを行います。なでしこにも当然、デバッガが搭載されており、今回の場合は8行目に、
デバッグ。
と書くだけでデバッグを行えます(デバッグ。の前に、インデントをそろえるための全角スペースが入っていることに注意してください)。
実行し、デバッグダイアログの[続ける]をクリックしながらバッファの中身を見てみると、肝心の「msgbox」関数が実行された後、{\n}(改行コード)が入っており、{\n}printを命令として認識してしまったようです。そのため、{\n}printは条件分岐の中にはないので、そのまま変数最新命令に格納されているmsgboxを実行してしまったのです。
ということで、次のように修正しました。
バッファは空。 文字列中は、いいえ。 最新命令は、空。 「msgbox "やってみました" print "やってみました"」を文字列分解して、反復 もし、バッファが「print」ならば、 最新命令はバッファ。 バッファは空。 違えば、もし、バッファが「msgbox」ならば、 最新命令はバッファ。 バッファは空。 違えば、もし、対象が「"」ならば、 もし、文字列中がはいならば、 文字列中は、いいえ。 もし、最新命令が「print」ならば、 バッファを表示。 バッファは空。 違えば、もし、最新命令が「msgbox」ならば、 バッファを言う。 バッファは空。 違えば、 文字列中は、はい。 バッファは空。 違えば、もし、対象が改行でなければ、 バッファは「{バッファ}{対象}」。
変更部分は、最終行直前の違えばを違えば、もし、対象が改行でなければ、したことです。さっきのデバッガで出てきた「{\n}」は改行を意味しますので、変更部分では変数「改行」が反復されていない時にバッファに文字を追加することにしました。つまり、改行コード以外の時に文字を追加するわけです。これを実行してみると、

思い通りの動作をしました。
print関数の使い方を理解したところで、いよいよコンパイラを作成していきましょう。

