はじめての処理系実装 その1
処理系の裏側をもっと覗いてみたくなったので自分で作ってみる事にした。
言語処理系は初めてなんだけど、去年からJavaScriptを深いところまでがっつり
勉強したおかげでなんとなくインタプリタのやっている事がイメージできる。
ここにはその作成経過を記録してみようと思う。
JavaでSchemeを実装する
何故Schemeを選んだかというと、仕様がシンプルで簡単そうだから。
加えて最近、Schemeを実装するのが流行っているみたいなので、
どうしても行き詰った時には解答を見るという逃げ道もありそうだしね。
ただSchemeの言語仕様自体をわかってないので、
Schemeの勉強も兼ねてってのも大きい。
CommonLispは少しかじったんだけどね。。。
じゃあCommonLispにしたらって話なんだけどSchemeの方が簡単そうなので。
まぁ実際には標準準拠ってとこまではできないだろうから
両方が入り混じった独自仕様になると思われる。
まぁマクロとかは概念すら解ってないのでパスの方向で。。。
とりあえず
Schemeの実装にあたって登場しそうなオブジェクトを適当に列挙してみる。
[S式、数値、文字列、シンボル、真偽値、リスト、アトム、
関数、マクロ、特別式、例外(Java)、解析器、評価器]
分類する
実装が簡単そうなデータ型だけ。
- S式
- アトム
- 数値
- 文字列
- シンボル
- 真偽値
- リスト
- 関数
- アトム
こんなところかな。
あまったオブジェクトはとりあえず保留。
S式から作成
Lispのデータは全ての型の扱いが同じなので、
S式をインターフェースにして各データ型を実装する。
とりあえず必要そうなメソッドは・・・
- eval(評価)
- toString(文字列化)
- equals(比較)
ってとこかな。
まぁevalさえあればデータ型を気にせず、
S式をインターフェースに評価できるだろう。
・・・とここで最初のポイント。
環境の扱いをどうするか。(変数の値を保持する環境の事ね)
シンボル毎に値を保持するべきか、
環境用オブジェクトをシングルトンで実装して、
実行時に静的参照を行うか、
それともevalの引数として渡してやるか。
なんとなく環境オブジェクトを引数渡しにしてみる。
まぁまたやりにくくなったら変更するって事で。
とりあえずコードに
public class Context { } public interface SymbolExp { SymbolExp eval(Context ctx); String toString(); }
これをベースにどんどん実装していくつもり。
多分、何日間かこんな感じで続きます。
できるあがる頃にはSchemeが普通に使えるように。。。なってるといいなぁ。