haXeで作るプログラミング言語(6)

今回は括弧演算子を作ります。括弧演算子って何?って思うかもしれませんが以下のようなものを括弧演算子と呼びます。

  • 括弧
() {} []

で、括弧の中身が空であることを表すために、vidという特別なものを作ります。voidにしたいのだけど、voidだとぶつかるのでvidにしています。

enum Exp {
   vid;
}

計算効率は落ちるんですけどただの構文木とするために通常の括弧も演算子にしてしまってます。
なぜするのかというと、抽象構文木ではなく構文木を扱うようにするためです。
抽象構文木構文木そのままではなくて、計算に必要な情報だけを残したものです。
そのほうが計算効率高くていいんですけど、今回作っている言語は構文木は唯の式を表すものですから、
構文木そのものであるほうが望ましいのです。

enum Exp {
	vid;
	nil;
	num(d:Int);
	sym(d:String);
	op(l:Exp, tag:String, r:Exp);
}

class Calc6 {
	static function main() {
		var c = new Calc6();
		trace(c.eval("a=(1+2)*3+() (a+1)*10"));
	}
	var opLs:Hash<Int>;
	var opRs:Hash<Int>;
	var opPs:Hash<String>;
	var opMs:Hash<Int>;
	var endPs:Hash<Int>;
	function new(){
		opLs = new Hash<Int>();
		opRs = new Hash<Int>();
		opPs = new Hash<String>();
		opMs = new Hash<Int>();
		endPs = new Hash<Int>();
		opLs.set("+", 10);
		opLs.set("-", 10);
		opLs.set("*", 20);
		opLs.set("/", 20);
		opRs.set("=", 5);
		opPs.set("(",")");
		opPs.set("[","]");
		opPs.set("{","}");
		opMs.set("(", 0);
		opMs.set("{", 0);
		opMs.set("[", 0);
		endPs.set(")", 0);
		endPs.set("]", 0);
		endPs.set("}", 0);
	}

	var src:String;
	var token:String;
	var token2:String;

	function lex():String {
		var r : EReg = ~/^[\r\n\t ]*([0-9]+|[+*\-\/=\[\]{}()]|[a-zA-Z_][a-zA-Z0-9]*)/;
		token2 = token;
		if(r.match(src)){
			token = r.matched(1);
			src = src.substr(r.matched(0).length);
			return token2;
		}
		token = "";
		return token2;
	}

	function eval(str:String):Int {
		var exp = parse(str);
		trace(exp);
		env = new Hash<Int>();
		return execute(exp);
	}

	function parse(str:String):Exp {
		src = str;
		lex();
		var rc = expn(0);
		while (token != "") {
			rc = op(rc, "@", expn(0));
		}
		return rc;
	}

	function expn(p:Int):Exp {
		if(endPs.exists(token)) return vid;
		var t = fact();
		var tagp:Int;
		while(true) {
			if(opLs.exists(token) && (tagp = opLs.get(token)) > p) {
				var tag = lex();
				t = op(t, "opL"+tag, expn(tagp));
				continue;
			}
			if(opRs.exists(token) && (tagp = opRs.get(token)) >= p) {
				var tag = lex();
				t = op(t, "opR"+tag, expn(tagp));
				continue;
			}
			break;
		}
		return t;
	}

	function fact():Exp {
		var t = lex();
		if(opPs.exists(t+"")) {

			var t2 = expn(0);
			if(lex() != opPs.get(t)) throw ("error");
			return op(nil, "opP()", t2);
		}
		var numReg : EReg = ~/[0-9]+/;
		if(numReg.match(t)) {
			return num(Std.parseInt(t));
		}
		return sym(t);
	}
	var env:Hash<Int>;
	
	function execute(exp:Exp):Int {
		switch(exp) {
		case vid: return 0;
		case nil: return 0;
		case num(d): return d;
		case op(l, tag, r):
			switch (tag) {
			case "@": execute(l); return execute(r);
			case "opP()": return execute(r);
			case "opL+": return execute(l) + execute(r);
			case "opL-": return execute(l) - execute(r);
			case "opL*": return execute(l) * execute(r);
			case "opL/": return cast(execute(l) / execute(r), Int);
			case "opR=":
				var rc = execute(r);
				switch(l){
				case sym(d):
					env.set(cast(d, String), rc);
					return rc;
				default: throw "error";
				}
			default: return 0;
			}
		case sym(d): return env.get(d);
		}
	}
}

Calc6.hxml

-swf Calc6.swf
-main Calc6