ネタがないからコンパイラを書こう。


たまーに、ネイティブコンパイラが書きたくなったりするので、そういうときはa24zの出番だよ。a24zは、おもに暇潰しのための言語です。ネタがなくなったら使われます。
自分でコンパイラ書けば、C言語では使えないような機能が色々使えます(最近そればっかだな)。

  • rotate
  • carry とか overflow とか
  • 64bit / 32bit 除算、 32*32 = 64bit乗算
  • 関数を超えるジャンプ
  • フレームいじる
  • その他

どれも別に必要じゃないけど。


あと、現状のa24zには、気に入らない部分が結構あって、というのは、つまり、使うのが面倒という部分なんだけど。
ヘッダと実装と、a24zの実装の三つを同時に変更しないといけないというのが特に痛い。あと、その他もろもろ。


ここで、a24zの出力を直接オブジェクトにすることによって、そこらへんの面倒な部分をリンカにまかせる、というのが便利かどうか試してみたいと思ったわけだ。


ここで、それってアレじゃないか(よーするに当初の目的を見失ってる)とか思うんだけど、いや、まあいいや。あんまり、そういうふうに考え出すと、何もしなくなるという説が。


というわけで、前回の反省を活かして、新a24zは以下のような感じで

module nanika; // <- 先頭

machine are // <- これが状態マシン
| int x
| int y
{
	// ここらへんに動作を
	int tmp = xxx;

	tmp = 8;
	if ( x ) { 
		yield;
	}
}


class base  // <- 基底クラスっぽいの (classかどうかは考える)
| int x
| int y
| int z


machine derived extends base
| int flag
| int zzz
{
}

こんな感じでやって。

  • 変なインデントがどうとかはもうどうでもいいや
  • 一個のファイルに複数書けるようにする
  • 状態変数共通できるところは共通化できるやうに
  • 型推論とかももういいや。

で、上のファイルをコンパイルすると、

struct nanika_are {
	int x;
	int y;
}

struct nanika_base {
	int x;
	int y;
	int z;
};

struct nanika_derived {
	int x;
	int y;
	int z;
	int flag;
	int zzz;			
}

union {
	struct nanika_are are;
	struct nanika_derived derived;
};


// 実体は生成する
nanika_are_init( int x, int y );
nanika_derived_init( int x, int y, int z, int flag, int zzz );

こんな感じのヘッダが出てくれれば嬉しい。


あとは、各種ABIに対応とか。

// C++ ABIに対応
namespace nanika {
struct are { ... };
struct base { ... };
struct derived { ... };

union {
	struct are are;
	struct derived derived;
};

// 中身はコンパイル時に生成する
are_init( int x, int y );
derived_init( int x, int y, int z, int flag, int zzz );
}

まあ、そんなあんな。


というわけで、機械語最適化をしよう!というか、もうパーサとかVMとかは書き飽きたところだ。


とりあえずパーサだけ
Parsecはすばらしい。


Haskellが素晴らしいかどうかは知らんけど、Parsecが素晴らしいことだけは、文句無いところであって、まあ、Parsecの素晴らしさは、Haskellの色々があるから実現できるわけで、そう考えると、Haskellは素晴らしいということになっていくんだけど、それは負けたような気がしないでもないというか。

そんなことより!
パースエラーは、

undefinedError name = fail $ "Undefined variable '" ++ name ++ "'"

これだけでよかったんだ、っていうか、二日ぐらいわからんかった。
fail、fail。モナドは失敗するかもしれないというふうに作られているということなんだろうか。