Io風味パーサ

かなり、適当ですが、Io風味のパーサを作ってみました。

a(b,c,d[][]) e f{} []

といった式をパースして、以下のような構造を作ります。

call{
 call{
  call{a(b,c,call{d[],[]}), e},
  f{}
 },
  []
}

Ioと互換ではなくて、拡張したり、手を抜いたりしてます。
Ioはかなり強力だということがわかりました。

<script>
function cparse (str) {
	var pop = function () {
		str = str.replace(/^[ \t\r\n]+/, "");
		var s = str.charAt(0);
		str = str.substring(1);
		return s;
	};
	var peek = function () {
		str = str.replace(/^[ \t\r\n]+/, "");
		return str.charAt(0);
	};
	var isParen = function(p) {
		return p=="(" || p=="[" || p=="{";
	}
	var isEndParen = function(p) {
		return p==")" || p=="]" || p=="}";
	}
	var cexp = function() {
		var m = list();
		while (peek() != "" && peek() != "," && !isEndParen(peek())) {
			var m = [m, list()];
			m.name = "call";
			m.type = "{";
		}
		return m;
	};

	var list = function () {
		var name = isParen(peek()) ? "" : atom();
		if (!isParen(peek())) return name;
		var type = pop();// (
		var end = {"(":")", "[":"]", "{":"}"}[type];

		var a = new Array();
		a.name = name;
		a.type = type;
		a.end = end;
		while(peek() != end) {
			a[a.length] = cexp();
			if (peek() == end) break;
			if (peek() == ",") {// 括弧の連続
				pop();
			} else {
				throw "error " + str;
			}
		}
		pop();// )
		return a;
	};
	var atom = function () {
		var cut = function(m) {
			var m;
			if (m = str.match(m)) {
				str = str.substring(m[0].length);
				return m[0];
			}
			return null;
		};
		var m;
		if (m = cut(/^[0-9]+/))                 return Number(m);
		if (m = cut(/^[a-zA-Z_][a-zA-Z0-9_]*/)) return m;
		if (m = cut(/^[\+\-\*\/]*/))            return m;
		throw "syntax error [" + str + "]";
	}
	return cexp();
};
Array.prototype.toString = function() {
	var type = "("; var end = ")";
	switch (this.type) {
	case "{": type = "{"; end = "}"; break;
	case "[": type = "["; end = "]"; break;
	}
	var str = ((this.name) ? this.name : "") + type;
	var sepa = ",";
	if (this.name == "call") {
		sepa = str = end = "";
	}

	var sepf = "";
	for (var i = 0; i < this.length; i++) {
		str += sepf + this[i]; sepf = sepa + " ";
	}
	return str + end;
};
Array.prototype.t_s = function() {
	var type = "("; var end = ")";
	switch (this.type) {
	case "{": type = "{"; end = "}"; break;
	case "[": type = "["; end = "]"; break;
	}
	var str = ((this.name) ? this.name : "") + type;
	var sepa = ",";
	var sepf = "";
	for (var i = 0; i < this.length; i++) {
		var a = (this[i]["t_s"]) ? this[i].t_s() : this[i];
		str += sepf + a; sepf = sepa + " ";
	}
	return str + end;
};
try {
	var c = cparse("if(a,b,c[][]) b c{}[]");
	alert("parse result:\n" + c + "\n" + c.t_s());
} catch(e) {
	alert(e);
}
</script>