演算子順位付きの再帰下降パーサ

なんか、プレビューを押したら固まった。そして、書いていた日記が消えてしまった。(泣


ここのところ、演算子順位法を使ったパーサは難しい。
ウームと思ってて、再帰下降パーサで優先順位付きの演算子をうまく扱えないかなぁと
考えてみたり、調べてみたりしてました。
で、


http://javascript.crockford.com/tdop/tdop.html


このへんのページを発見。すごく、やりたいことに近いことが書いてあるわけですが、
これだこれ。ってことで、ソース見てみました。
要は、exp0,exp10,exp20という演算子の関数を呼び出して同じように再帰下降でパーサを書くとするなら、その数字をパラメータ化します。exp(0),exp(10),exp(20)というように。
それだけだと、呼び出しはexp(0),exp(10),exp(20)のように呼んでいかないといけません。
でもここで、演算子の順位を演算子順位法でやったときのように、比較してやりますと
exp(0)の次にexp(20)をイキナリ呼び出して使ったり出来るようになります。
その分高速で、かつシンプルになるようです。


自分が作ったものは、以下のアドレスで試せます。
http://sakurai.s59.xrea.com/compact/op/t.html


以下ソースです。

<html>
<script>
function run() {
	var t = document.getElementById("in").value;
	try {
		t = parse(t);
	} catch(e){t = e}
	document.getElementById("out").value = t;
}

Array.prototype.toString = function() { return "[" + this.join(",") + "]"; }
function parse(str) {
	var tokens = [];
	str.replace(/\+\+|[\r\n]+|[-\+*\/\(\)\[\]\{\}\=\;\:]|[^-\+*\/\(\)\[\]\{\}\s\=\;\:]+|[\s]+/g,function(s) {
		if(s.match(/^[^\s]+/)) tokens.push(s);
	});
	function xpxp(n) {
		var t1 = t;
		var op = token;
		var p = infixs[op][0];
		if(p <= n) return false;
		advance();
		var e = ex();
		var op2 = token;
		advance();
		if (infixs[op][2] != op2) throw "error "+infixs[op][2] + " "+op2;
		t = [t1, op, e, op2];
		return true;
	}
	function xox(n) {
		var op = token;
		var p = infixs[op][0];
		if(p <= n) return false;
		advance();
		t = [t, op, exp(p)];
		return true;
	}
	function xoy(n) {
		var op = token;
		var p = infixs[op][0];
		if(p < n) return false;
		advance();
		t = [t, op, exp(p)];
		return true;
	}
	function xo(n) {
		var op = token;
		var p = infixs[op][0];
		if(p <= n) return false;
		advance();
		t = [t, op];
		return true;
	}

	function pxp(n) {
		var p1 = t;
		var e = ex();
		if(prefixs[p1][2] != token) throw "error "+prefixs[p1][2]+" "+token;
		t = [p1, e, token];
		advance();
	}
	function opxpx(n) {
		var op = t;
		if(prefixs[op][2] != token) throw "error "+prefixs[op][2]+" "+token;
		var p1 = token;
		advance();
		var e = ex();
		if(prefixs[op][3] != token) throw "error "+prefixs[op][3]+" "+token;
		var p2 = token;
		advance();
		t = [op, p1, e, p2, ex()];
		advance();
	}
	function oy(n) {
		t = [t, exp(prefixs[t][0])];
	}

	var infixs = {"*":[20, xox], "/":[20, xox], "-":[10, xox], "+": [10, xox], "=":[1, xoy],":":[1, xoy], "++":[70,xo], ";":[5,xo], "(":[70,xpxp,")"],"[":[70,xpxp,"]"],"{":[70,xpxp,"}"]};
	var prefixs = {"-":[70, oy], "(":[70,pxp,")"], "{":[70,pxp,"}"], "[":[70,pxp,"]"],"if":[80,opxpx,"(",")"]};

	var pxpe = {};
	for(var i in prefixs) {
		if(prefixs[i][1]==pxp) pxpe[prefixs[i][2]]=i;
		if(prefixs[i][1]==opxpx) pxpe[prefixs[i][3]]=prefixs[i][2];
	}
	for(var i in infixs) if(infixs[i][1]==xpxp) pxpe[infixs[i][2]]=i;

	var token;
	var t;
	var index = 0;
	function advance() { token = tokens[index++]; }
	function ex() {
		var e = exp(0);
		while(token != undefined && !(token in pxpe) && !(token in infixs) && !(token in prefixs)) {
			var e2 = e;
			e = [e2,"@", exp(0)];
		}
		return e;
	}
	function exp(n) {
		t = token;
		if (t in pxpe) return null;
		advance();
		if (t in prefixs) prefixs[t][1](n);
		while(token in infixs && infixs[token][1](n)){}
		return t;
	}
	advance();
	return ex();
	return e;
}
</script>

<body>
<input type=button onclick=run()><br />
<textarea id=in cols=80 rows=5>add:function(a;b){a+b}
sub:function(a;b){a-b}
</textarea><br />
<textarea id=out cols=80 rows=25>
</textarea><br />
</body>
</html>