最適化ができなかったのは、自分で昔書いたコードがいらんことをしていただけだった。

  /* todo: これ何? */
  *subtrees = false;
  return NULL;

うーん。理解できなかったのに、なんで*subtreesをfalseにしてたんだろうか…自分は。
理解できないものを勝手にいじってはいけません。


まあいいか。ILogScriptでコンパイラを書く利点のひとつに、コンパイラ自身が動的言語で書かれることになる、というところがある。あとから機能を拡張するのが非常に簡単なわけだ。この利点を活かして、コンパイラ自身のパーサーと全く同じ能力を持ったマクロを書けるようになったりするかもしれない。


例えば、こんな風にする。


まず、言語の内部構文木のインターフェースを決めておく。例えば、soreitの場合は、「build_treeとtypecheckメソッドを持つ」と、してある。

function integer_constant(v)
{
	return object {
		// 整数定数の木オブジェクト
		var val = v;
		function typecheck( env ) {
			env.unifier.push( types.integer_type_node );
		}

		function to_str() {
			return @val.to_str();
		}

		function build_tree( env ) {
			return @val;
		}
	};
}

んで、言語側に、「キーワード登録機能」のようなものを付けて

# キーワード文でキーワード追加
keyword nanika_keyword = "nanika.ilog"

キーワードとスクリプトは結び付けられるようにしておく。んで、そのスクリプトモジュールには、parse関数の定義を入れるように決めとく。んで、このparse関数は、さっき決めたインターフェースを持ったオブジェクトを返すようにしておく。

// nanika.ilog

function parse( lexer ) {

	...

	return object { 
		function typecheck(env){...}
		funciton build_tree(env){...}
	};
}

これで、言語側で、

def func() :
	nanika_keyword {....}

こんなふうにさっき登録したキーワードが出現したら、そのときのレキサーを、さっきのparse関数に渡す、としておく。それで、まあ、あれのナニがどうしたりできる、というわけだ。(面倒になってきたらしい)

keyword bf = "brainf__k.ilog"

def func( i ):
let:
	a = array()
in:
	# 配列 a の上でbrain f**kを動かす
	bf ( a ) { + + + + - - - - < < > > }

みたいなことも不可能ではないはず。何の役に立つのかは知らんが。


と、いうような話。


というか、ILogScriptの元ネタであるNeta Languageは、もともとこういうことをやりたくて、思い付いたものだったので、このぐらいできないと存在理由が無いんだけど。
それで、その元ネタを書いたのが、ちょうど二年くらい前なんだよな。
http://morihyphen.hp.infoseek.co.jp/log/03_11.html#03_11_23
あれから、二年かけて、ようやく、それなりに使えるものになってきたのか、とか考えるとそれなりにアレ。


でも、メタプログラミングが役に立ちそうな場面なんて、この二年間で一度としてなかったな。とか考えると、この二年は全くの無駄だったとも言えるわけで。なんだそりゃ。


そこらへんは、結局、D言語のように、「便利な部分だけを簡単に使えますよ」ぐらいがちょうどいいんではないか、と。