プログラミング言語をつくってみる②


実行関数を作る

パーサができたので、パーサを通して得られる抽象木をたどって、プログラムを実行する関数を作りました。パーサで得られる抽象木の節には、どのルールで作るられたかが文字列で記録されています。それをもとに処理方法を変える方針で作りました。

初めは一つの関数内で分岐しようかとも思ったのですが、今回は JavaScript でわりと自由なので、先に関数を設定してあとはその関数を呼んでいくだけという方式にしました。(まぁ、関数の設定は switch 文ですが)

SsEvalFactory 関数で、節の名前を見て、一つずつ eval 関数を設定していっています。

コード1: 実行関数

SsEnvironment クラスは、実行環境を入れておくためのクラスで、とりあえずグローバル変数を入れておくためのオブジェクトを一つ持たせています。そして、各評価関数(eval)関数は引数に SsEnvironment のオブジェクトを渡していくようにしています。

eval 関数は、式であれば単純にその式の評価結果値を返すようにしており、上位の式はその値をもとに計算して、更に値を返す感じです。文は、とりあえず null を返しています。

作ってみて、微妙な実装だなぁと思ったため、パーサジェネレータの生成するコードのところでファクトリーを通してオブジェクトを生成するような仕組みや、シンボル名を見て生成する節のクラスを変えるといった方式にしようかと思います。

実行してみる

以下のコードで、src 変数内のソースコードを実行できます。今回、出力系がまったくなく、結果の確認ができなかったため、SsEnvironment のオブジェクト env には、一つだけ、画面出力用の関数 print を設定しました。これで、最低限出力はできるようにしました。

以下のコードを実行してみると、うまくいきました。JavaScript じゃないですよ。今回作った言語です。

実行結果は以下のとおりです。

ちゃんと、(a+i) も計算されていますし、意図通り文字列も結合されています。いい感じです。

ちなみに、とりあえず動くものを作ってみたかっただけのため、宣言していない変数の使用とかそういうエラーは一切出ません。そのうち、中間コードの生成をやってみようと思っていますので、そのときにちゃんと考えます。

とにかく、ここまで来るとかなり楽しいです。

あっ、break とか continue とか return とかできない…

さて、ここまで 2 週間でできる ! スクリプト言語を中心に読み進めながら作って来たのですが、この実装ですと、break などの構文を追加しようとするとうまくいかないことに気が付きました。ただ、残念ながらこちらの本では脱出構文に対応していませんでした。

そこで、いい方法はないものかと、もう一冊の方を読み返してみると、対応はしていたのですが、評価関数の戻り値に設定して一つずつ返していくという実装になっていました。ん~と思いつつ、これしか無いようですので、この方法を採用しました。

eval 関数は必ず SsResult 型のオブジェクトを返すようにし、ブロックや for 文、while 文で相応の処理をするようにしました。将来を見越して、return 文の下準備もしてあります。

コード2: SsResult 型
コード3: ブロックと while 文の処理

これで、break と continue がうまく動作するようになりました。goto の実装予定はないですし、break 、continue 、return 以外に(例外処理を除いて)こういった命令を実装予定ありませんので、ひとまずこれで良さそうです。ラベル付きの break とかも、もうちょっと判断を細かくすれば対応できそうです(作る気は無いですが)。

まとめ

最低限の動作までできました。次は、関数を実装しようと思います。ブロックスコープとかそのへんもやってみようと思います。