バイナリツリーバリデータ改

全ての値をバイナリツリーにしてしまうと、switch文だけで文法をチェックできます。
エラーを発見したらthrowすることにすると、returnの値がtrueかfalseかとか
関係なくなるので、アクションとか追加できそうな雰囲気です。
ガードとか無いので、match構文なんていらない!ってところまではならないかもしれないですが、
いい感じなのではないかと。
これを使って、インタプリタや、スタックマシンへのコンパイラを作ってみると、アクションを
どう追加したら良いかが分かってくると思います。

<script>
function BTree(a,b,c) {
	this.l = a;
	this.op = b;
	this.r = c;
}
BTree.prototype.toString = function() {
	return "B("+this.l+","+this.op+","+this.r+")";
}
function toB(a) {
	switch(typeof(a)) {
	case "string":
	case "number": return new BTree(a, typeof(a), null);
	}
	return a;
}
function B(a,b,c) {
	return new BTree(toB(a),b,toB(c));
}

// 2分木の再帰下降バリデータ。
function valid(data) {
	return statement(data);
}
function statement(data) {
	switch (data.op) {
	case "if": return exp(data.l), else_(data.r);
	case "C":  return valid(data.l), valid(data.r);
	default:   return exp(data);
	}
}
function else_(data) {
	switch(data.op) {
	case "else": return exp(data.l), exp(data.r);
	default:     return exp(data);
	}
}

function id(data) {
	switch(data.op) {
	case "number":
	case "string": return data;
	default:       throw "error";
	}
}
function assignLeft(data) {
	switch(data.op) {
	case "[]": return arr(data);
	default:   return id(data);
	}
}
function arr(data) {
	switch(data.op) {
	case "[]": return assignLeft(data.l), exp(data.r);
	default:   throw "error";
	}
}
function exp(data) {
	switch (data.op) {
	case "=": return assignLeft(data.l), exp(data.r);
	case "+":
	case "*": return exp(data.l), exp(data.r);
	default:  return id(data);
	}
}
function p(d) {
	document.write("<b>"+d+"</b><br/>");
}

function test(data) {
	try {
		valid(data);
	} catch (e) {
		document.write(e+" "+data+"<br>");
	}
}

test(B(1,"+", B(2, "*", 3)));
test(B(1,"*", B(2, "+", 3)));
test(B(1,"if", B(2, "else", 3)));
test(B(1,"if", B(2, "els", 3)));
test(B(1,"if", B(2, "else", B(3,"else",4) )));
test(B(1,"if", 3));
test(B(1,"C",B(1,"if", 3)));
test(B(1,"else",2));
test(B("a","=",2));
test(B(B("a","=","b"),"=",2));
test(B("a","=",B("b","=",2)));
test(B(B("a","[]",2),"=",2));
test(B(B("a","[]",B("a","=","b")),"=",2));
</script>